From 365338e4c088143893e888d219f1a1f173498146 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 15 Jul 2021 14:40:02 +0300 Subject: [PATCH 01/73] Fire user updated notification when updating user's private RSA key. --- CHANGELOG.md | 3 ++- lib/service/Health.dart | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b038083..04231494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Added +- Fire user updated notification when updating user's private RSA key. ## [2.10.31] - 2021-07-17 ### Changed @@ -14,7 +16,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix gradle build for Android [#670](https://github.com/rokwire/safer-illinois-app/issues/670). - Check string parameters match case insensitively [#668](https://github.com/rokwire/safer-illinois-app/issues/668). - ## [2.10.30] - 2021-06-16 ### Added - Added Consent Health Provider Vaccine Information flag and related UI [#661](https://github.com/rokwire/safer-illinois-app/issues/661). diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 9a877a2b..eacad6ab 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -592,6 +592,7 @@ class Health with Service implements NotificationsListener { Future setUserPrivateKey(PrivateKey privateKey) async { if (await _saveUserPrivateKey(privateKey)) { _userPrivateKey = privateKey; + _notify(notifyUserUpdated); _refresh(_RefreshOptions.fromList([_RefreshOption.history])); return true; } From 926b986507081fc2bf0725abaa014610eb761243 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 30 Jul 2021 08:22:57 +0300 Subject: [PATCH 02/73] Updated test intervals for undergraduate students and other (#676). --- assets/health.rules.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index dee6683d..a27c7bdf 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -7,8 +7,8 @@ }, "intervals": { - "DefaultTestMonitorInterval": 8, - "UndergraduateTestMonitorInterval": 8, + "DefaultTestMonitorInterval": 4, + "UndergraduateTestMonitorInterval": 2, "UserTestMonitorInterval": null, "TestMonitorInterval": { From d4606c23acd0bef7dad5df688725a864884eb144 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 30 Jul 2021 08:24:18 +0300 Subject: [PATCH 03/73] Updated CHANGELOG.md (#676). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04231494..93a4a90e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added - Fire user updated notification when updating user's private RSA key. +### Changed +- Updated test intervals for undergraduate students and others [#676](https://github.com/rokwire/safer-illinois-app/issues/676). ## [2.10.31] - 2021-07-17 ### Changed From c1de8ea63a9c6feed6ace2d85cb240376d7870bf Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 2 Aug 2021 14:55:18 +0300 Subject: [PATCH 04/73] Added weekdays extension capability for health rule intervals (#678). --- assets/health.rules.json | 21 ++++++- lib/model/Health.dart | 123 ++++++++++++++++++++++++++++++++++----- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index a27c7bdf..cb0e03d2 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -19,7 +19,24 @@ "success": "UndergraduateTestMonitorInterval", "fail": "DefaultTestMonitorInterval" } - } + }, + + "DefaultTestMonitorWeekdaysExtent": ["Fri", "Sat", "Sun", "Mon"], + "UndergraduateTestMonitorWeekdaysExtent": ["Sat", "Sun"], + + "TestMonitorWeekdaysExtent": { + "condition": "test-user", "params": { "card.role": "Undergraduate", "card.student_level": "1U" }, + "success": "UndergraduateTestMonitorWeekdaysExtent", + "fail": "DefaultTestMonitorWeekdaysExtent" + }, + + "Mon": 1, + "Tue": 2, + "Wed": 3, + "Thu": 4, + "Fri": 5, + "Sat": 6, + "Sun": 7 }, "constants": { @@ -190,7 +207,7 @@ "test-monitor": { "condition": "require-test", "params": { - "interval": { "min": 0, "max": "TestMonitorInterval", "scope": "future", "current": true } + "interval": { "min": 0, "max": "TestMonitorInterval", "max-weekdays-extent": "TestMonitorWeekdaysExtent", "scope": "future", "current": true } }, "success": { "code": "yellow", diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 4afb08cd..37e95bd0 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -3577,6 +3577,10 @@ abstract class _HealthRuleInterval { else if (json is String) { return HealthRuleIntervalReference.fromJson(json); } + else if (json is List) { + try { return HealthRuleIntervalSet.fromJson(json.cast()); } + catch (e) { print(e?.toString()); } + } else if (json is Map) { if (HealthRuleIntervalCondition.isJsonCompatible(json)) { try { return HealthRuleIntervalCondition.fromJson(json.cast()); } @@ -3590,7 +3594,7 @@ abstract class _HealthRuleInterval { return null; } - bool match(int value, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); int value({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); bool valid({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); int scope({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); @@ -3607,6 +3611,21 @@ abstract class _HealthRuleInterval { } return result; } + + static int applyWeekdayExtent(_HealthRuleInterval weekdayExtent, DateTime orgDate, int value, int step, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params } ) { + if ((weekdayExtent != null) && (orgDate != null) && (value != null) && (step != null)) { + //DateTime dateExt = orgDate.add(Duration(days: value + step)); + DateTime dateExt = DateTime(orgDate.year, orgDate.month, orgDate.day + value + step, orgDate.hour, orgDate.minute, orgDate.second); + while (weekdayExtent.match(dateExt.weekday, orgDate: orgDate, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { + value += step; + // dateExt = dateExt.add(Duration(days: step)); + dateExt = DateTime(dateExt.year, dateExt.month, dateExt.day + step, orgDate.hour, orgDate.minute, orgDate.second); + } + return value; + } + return null; + } + } enum HealthRuleIntervalOrigin { historyDate, referenceDate } @@ -3631,7 +3650,7 @@ class HealthRuleIntervalValue extends _HealthRuleInterval { int get hashCode => (_value?.hashCode ?? 0); - @override bool match(int value, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) {return (_value == value); } + @override bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) {return (_value == value); } @override int value({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return _value; } @override bool valid({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return (_value != null); } @override int scope({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return null; } @@ -3655,14 +3674,18 @@ class HealthRuleInterval extends _HealthRuleInterval { final int _scope; final bool _current; final HealthRuleIntervalOrigin _origin; + final _HealthRuleInterval _minWeekdaysExtent; + final _HealthRuleInterval _maxWeekdaysExtent; - HealthRuleInterval({_HealthRuleInterval min, _HealthRuleInterval max, _HealthRuleInterval value, int scope, bool current, HealthRuleIntervalOrigin origin}) : + HealthRuleInterval({_HealthRuleInterval min, _HealthRuleInterval max, _HealthRuleInterval value, int scope, bool current, HealthRuleIntervalOrigin origin, _HealthRuleInterval minWeekdaysExtent, _HealthRuleInterval maxWeekdaysExtent }) : _min = min, _max = max, _value = value, _scope = scope, _current = current, - _origin = origin; + _origin = origin, + _minWeekdaysExtent = minWeekdaysExtent, + _maxWeekdaysExtent = maxWeekdaysExtent; factory HealthRuleInterval.fromJson(Map json) { return (json != null) ? HealthRuleInterval( @@ -3672,6 +3695,8 @@ class HealthRuleInterval extends _HealthRuleInterval { scope: _scopeFromJson(json['scope']), current: json['current'], origin: _originFromJson(json['origin']), + minWeekdaysExtent: _HealthRuleInterval.fromJson(json['min-weekdays-extent']), + maxWeekdaysExtent: _HealthRuleInterval.fromJson(json['max-weekdays-extent']), ) : null; } @@ -3682,7 +3707,9 @@ class HealthRuleInterval extends _HealthRuleInterval { (o._value == _value) && (o._scope == _scope) && (o._current == _current) && - (o._origin == _origin); + (o._origin == _origin) && + (o._minWeekdaysExtent == _minWeekdaysExtent) && + (o._maxWeekdaysExtent == _maxWeekdaysExtent); int get hashCode => (_min?.hashCode ?? 0) ^ @@ -3690,25 +3717,39 @@ class HealthRuleInterval extends _HealthRuleInterval { (_value?.hashCode ?? 0) ^ (_scope?.hashCode ?? 0) ^ (_current?.hashCode ?? 0) ^ - (_origin?.hashCode ?? 0); + (_origin?.hashCode ?? 0) ^ + (_minWeekdaysExtent?.hashCode ?? 0) ^ + (_maxWeekdaysExtent?.hashCode ?? 0); @override - bool match(int value, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { if (value != null) { if (_min != null) { int minValue = _min.value(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); + minValue = _HealthRuleInterval.applyWeekdayExtent(_minWeekdaysExtent, orgDate, minValue, -1, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params) ?? minValue; if ((minValue == null) || (minValue > value)) { return false; } } if (_max != null) { int maxValue = _max.value(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); + maxValue = _HealthRuleInterval.applyWeekdayExtent(_maxWeekdaysExtent, orgDate, maxValue, 1, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params) ?? maxValue; if ((maxValue == null) || (maxValue < value)) { return false; } } if (_value != null) { int valueValue = _value.value(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); + + int minValue = _HealthRuleInterval.applyWeekdayExtent(_minWeekdaysExtent, orgDate, valueValue, -1, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); + if ((minValue != null) && (minValue > value)) { + return false; + } + int maxValue = _HealthRuleInterval.applyWeekdayExtent(_maxWeekdaysExtent, orgDate, valueValue, 1, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); + if ((maxValue != null) && (maxValue < value)) { + return false; + } + if ((valueValue == null) || (valueValue != value)) { return false; } @@ -3768,6 +3809,62 @@ class HealthRuleInterval extends _HealthRuleInterval { } } +/////////////////////////////// +// HealthRuleIntervalSet + +class HealthRuleIntervalSet extends _HealthRuleInterval { + List<_HealthRuleInterval> _entries; + + HealthRuleIntervalSet({List<_HealthRuleInterval> entries}) : + _entries = entries; + + factory HealthRuleIntervalSet.fromJson(List json) { + List<_HealthRuleInterval> entries; + if (json != null) { + entries = <_HealthRuleInterval>[]; + for (dynamic jsonEntry in json) { + _HealthRuleInterval entry = _HealthRuleInterval.fromJson(jsonEntry); + if (entry != null) { + entries.add(entry); + } + } + } + return (entries != null) ? HealthRuleIntervalSet(entries: entries) : null; + } + + bool operator ==(o) => + (o is HealthRuleIntervalSet) && + DeepCollectionEquality().equals(o._entries, _entries); + + int get hashCode => + (DeepCollectionEquality().hash(_entries) ?? 0); + + @override + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { + for (_HealthRuleInterval entry in _entries) { + if (entry.match(value, orgDate: orgDate, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { + return true; + } + } + return false; + } + + @override + bool valid({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { + for (_HealthRuleInterval entry in _entries) { + if (!entry.valid(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { + return false; + } + } + return 0 < _entries.length; + } + + @override int value({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return null; } + @override int scope({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return null; } + @override bool current({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return null; } + @override HealthRuleIntervalOrigin origin({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return null; } +} + /////////////////////////////// // HealthRuleIntervalReference @@ -3794,8 +3891,8 @@ class HealthRuleIntervalReference extends _HealthRuleInterval { } @override - bool match(int value, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { - return _referenceInterval(rules: rules, params: params)?.match(value, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params) ?? false; + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { + return _referenceInterval(rules: rules, params: params)?.match(value, orgDate: orgDate, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params) ?? false; } @override bool valid({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { return _referenceInterval(rules: rules, params: params)?.valid(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params) ?? false; } @@ -3843,10 +3940,10 @@ class HealthRuleIntervalCondition extends _HealthRuleInterval with HealthRuleCon (failInterval?.hashCode ?? 0); @override - bool match(int value, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }) { HealthRuleConditionResult conditionResult = evalCondition(history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params); _HealthRuleInterval interval = (conditionResult?.result != null) ? (conditionResult.result ? successInterval : failInterval) : null; - return interval?.match(value, history: history, historyIndex: historyIndex, referenceIndex: conditionResult?.referenceIndex ?? referenceIndex, rules: rules, params: params) ?? false; + return interval?.match(value, orgDate: orgDate, history: history, historyIndex: historyIndex, referenceIndex: conditionResult?.referenceIndex ?? referenceIndex, rules: rules, params: params) ?? false; } @override @@ -3996,7 +4093,7 @@ abstract class HealthRuleCondition { //#572 Building access calculation issue //int difference = entryDateMidnightLocal.difference(originDateMidnightLocal).inDays; int difference = AppDateTime.midnightsDifferenceInDays(originDateMidnightLocal, entryDateMidnightLocal); - if (interval.match(difference, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { + if (interval.match(difference, orgDate: originDateMidnightLocal, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { // check filters before returning successfull match if (_matchValue(historyType, HealthHistoryType.test)) { @@ -4047,7 +4144,7 @@ abstract class HealthRuleCondition { //#572 Building access calculation issue //int difference = AppDateTime.todayMidnightLocal.difference(originDateMidnightLocal).inDays; int difference = AppDateTime.midnightsDifferenceInDays(originDateMidnightLocal, AppDateTime.todayMidnightLocal); - if (currentInterval.match(difference, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { + if (currentInterval.match(difference, orgDate: originDateMidnightLocal, history: history, historyIndex: historyIndex, referenceIndex: referenceIndex, rules: rules, params: params)) { return true; } } From bd8baffec0ed8e897f5b7295954b68677c807aa8 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 2 Aug 2021 14:56:47 +0300 Subject: [PATCH 05/73] Updated CHANGELOG.md (#678). --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a4a90e..8d74626f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added - Fire user updated notification when updating user's private RSA key. +- Added weekdays extension capability for health rule intervals [#678](https://github.com/rokwire/safer-illinois-app/issues/678). ### Changed - Updated test intervals for undergraduate students and others [#676](https://github.com/rokwire/safer-illinois-app/issues/676). From 4bfa146ac81f0ed881d6efbbe272e864bb72eb79 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 2 Aug 2021 15:02:19 +0300 Subject: [PATCH 06/73] version: 2.10.32+1032 --- CHANGELOG.md | 1 + pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d74626f..ca80c64a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +## [2.10.32] - 2021-08-02 ### Added - Fire user updated notification when updating user's private RSA key. - Added weekdays extension capability for health rule intervals [#678](https://github.com/rokwire/safer-illinois-app/issues/678). diff --git a/pubspec.yaml b/pubspec.yaml index 1f04f17a..1da67754 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.31+1031 +version: 2.10.32+1032 environment: sdk: ">=2.2.0 <3.0.0" From 0d3a18cbc762e6661969eea2fc83b38c35392047 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 3 Aug 2021 09:21:30 +0300 Subject: [PATCH 07/73] Force onboarding from app config (#681). --- lib/main.dart | 34 +++++++++++++++++++++++++--------- lib/service/Config.dart | 31 ++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 863b7557..3db2799d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -116,6 +116,7 @@ class App extends StatefulWidget { class _AppState extends State implements NotificationsListener { + String _lastRunVersion; String _upgradeRequiredVersion; String _upgradeAvailableVersion; Key key = UniqueKey(); @@ -128,6 +129,7 @@ class _AppState extends State implements NotificationsListener { Onboarding.notifyFinished, Config.notifyUpgradeAvailable, Config.notifyUpgradeRequired, + Config.notifyOnboardingRequired, Organizations.notifyOrganizationChanged, Organizations.notifyEnvironmentChanged, UserProfile.notifyProfileDeleted, @@ -135,18 +137,13 @@ class _AppState extends State implements NotificationsListener { AppLivecycle.instance.ensureBinding(); + _lastRunVersion = Storage().lastRunVersion; _upgradeRequiredVersion = Config().upgradeRequiredVersion; _upgradeAvailableVersion = Config().upgradeAvailableVersion; - // This is just a placeholder to take some action on app upgrade. - String lastRunVersion = Storage().lastRunVersion; - if ((lastRunVersion == null) || (lastRunVersion != Config().appVersion)) { - - // Force unboarding to concent vaccination (#651) - if (AppVersion.compareVersions(lastRunVersion, '2.10.28') < 0) { - Storage().onBoardingPassed = false; - } - + _checkForceOnboarding(); + + if ((_lastRunVersion == null) || (_lastRunVersion != Config().appVersion)) { Storage().lastRunVersion = Config().appVersion; } @@ -211,6 +208,20 @@ class _AppState extends State implements NotificationsListener { Navigator.pushAndRemoveUntil(context, routeToHome, (_) => false); } + bool _checkForceOnboarding() { + // Action: Force unboarding to concent vaccination (#651, #681) + String onboardingRequiredVersion = Config().onboardingRequiredVersion; + if ((Storage().onBoardingPassed == true) && + (_lastRunVersion != null) && + (onboardingRequiredVersion != null) && + (AppVersion.compareVersions(_lastRunVersion, onboardingRequiredVersion) < 0) && + (AppVersion.compareVersions(onboardingRequiredVersion, Config().appVersion) <= 0)) { + Storage().onBoardingPassed = false; + return true; + } + return false; + } + // NotificationsListener @override @@ -228,6 +239,11 @@ class _AppState extends State implements NotificationsListener { _upgradeAvailableVersion = param; }); } + else if (name == Config.notifyOnboardingRequired) { + if (_checkForceOnboarding()) { + _resetUI(); + } + } else if (name == Organizations.notifyOrganizationChanged) { _resetUI(); } diff --git a/lib/service/Config.dart b/lib/service/Config.dart index 2d4b0be4..3148d6d3 100644 --- a/lib/service/Config.dart +++ b/lib/service/Config.dart @@ -41,6 +41,7 @@ class Config with Service implements NotificationsListener { static const String notifyUpgradeRequired = "edu.illinois.rokwire.config.upgrade.required"; static const String notifyUpgradeAvailable = "edu.illinois.rokwire.config.upgrade.available"; + static const String notifyOnboardingRequired = "edu.illinois.rokwire.config.onboarding.required"; static const String notifyConfigChanged = "edu.illinois.rokwire.config.changed"; Map _config; @@ -94,6 +95,7 @@ class Config with Service implements NotificationsListener { if (_config != null) { _checkUpgrade(); + _checkOnboarding(); _updateFromNet(); } else if (Organizations().organization != null) { @@ -105,6 +107,7 @@ class Config with Service implements NotificationsListener { NotificationService().notify(notifyConfigChanged, null); _checkUpgrade(); + _checkOnboarding(); } } else { @@ -164,7 +167,7 @@ class Config with Service implements NotificationsListener { for (int index = jsonList.length - 1; index >= 0; index--) { Map cfg = jsonList[index]; - if (AppVersion.compareVersions(cfg['mobileAppVersion'], _packageInfo.version) <= 0) { + if (AppVersion.compareVersions(cfg['mobileAppVersion'], appVersion) <= 0) { _decodeSecretKeys(cfg); return cfg; } @@ -196,6 +199,7 @@ class Config with Service implements NotificationsListener { NotificationService().notify(notifyConfigChanged, null); _checkUpgrade(); + _checkOnboarding(); } }); } @@ -239,7 +243,7 @@ class Config with Service implements NotificationsListener { String get upgradeRequiredVersion { dynamic requiredVersion = _upgradeStringEntry('required_version'); - if ((requiredVersion is String) && (AppVersion.compareVersions(_packageInfo.version, requiredVersion) < 0)) { + if ((requiredVersion is String) && (AppVersion.compareVersions(appVersion, requiredVersion) < 0)) { return requiredVersion; } return null; @@ -248,7 +252,7 @@ class Config with Service implements NotificationsListener { String get upgradeAvailableVersion { dynamic availableVersion = _upgradeStringEntry('available_version'); bool upgradeAvailable = (availableVersion is String) && - (AppVersion.compareVersions(_packageInfo.version, availableVersion) < 0) && + (AppVersion.compareVersions(appVersion, availableVersion) < 0) && !Storage().reportedUpgradeVersions.contains(availableVersion) && !_reportedUpgradeVersions.contains(availableVersion); return upgradeAvailable ? availableVersion : null; @@ -291,6 +295,23 @@ class Config with Service implements NotificationsListener { } } + // Onboarding + + String get onboardingRequiredVersion { + dynamic requiredVersion = onboardingInfo['required_version']; + if ((requiredVersion is String) && (AppVersion.compareVersions(requiredVersion, appVersion) <= 0)) { + return requiredVersion; + } + return null; + } + + void _checkOnboarding() { + String value; + if ((value = this.onboardingRequiredVersion) != null) { + NotificationService().notify(notifyOnboardingRequired, value); + } + } + // Assets cache path Directory get appDocumentsDir { @@ -333,9 +354,9 @@ class Config with Service implements NotificationsListener { Map get secretOsf { return secretKeys['osf'] ?? {}; } Map get secretHealth { return secretKeys['health'] ?? {}; } - Map get upgradeInfo { return (_config != null) ? (_config['upgrade'] ?? {}) : {}; } - Map get settings { return (_config != null) ? (_config['settings'] ?? {}) : {}; } + Map get upgradeInfo { return (_config != null) ? (_config['upgrade'] ?? {}) : {}; } + Map get onboardingInfo { return (_config != null) ? (_config['onboarding'] ?? {}) : {}; } String get assetsUrl { return otherUniversityServices['assets_url']; } // "https://rokwire-assets.s3.us-east-2.amazonaws.com" String get feedbackUrl { return otherUniversityServices['feedback_url']; } // "https://forms.illinois.edu/sec/1971889" From 95c8e3041657c8ed9d1bfec10fe75fc44bd3db97 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 3 Aug 2021 09:22:54 +0300 Subject: [PATCH 08/73] Updated CHANGELOG.md (#681) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca80c64a..7ce0bd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Added +- Force onboarding from app config [#681](https://github.com/rokwire/safer-illinois-app/issues/681). + ## [2.10.32] - 2021-08-02 ### Added - Fire user updated notification when updating user's private RSA key. From d77a3eb1141e515f46b8dfc964991cba332a2774 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 3 Aug 2021 09:26:05 +0300 Subject: [PATCH 09/73] version: 2.10.33+1033 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce0bd2f..801d224e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.33] - 2021-08-03 ### Added - Force onboarding from app config [#681](https://github.com/rokwire/safer-illinois-app/issues/681). diff --git a/pubspec.yaml b/pubspec.yaml index 1da67754..4c825867 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.32+1032 +version: 2.10.33+1033 environment: sdk: ">=2.2.0 <3.0.0" From a3d7b7192faa97f2866e54c5e8cc4dbb1110ecff Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Wed, 4 Aug 2021 13:39:57 +0300 Subject: [PATCH 10/73] Feature/issue 680 (#683) * Change label from requesting vaccine and test again [#680] * Update CHANGELOG.md [#680] --- CHANGELOG.md | 2 ++ assets/strings.en.json | 2 +- assets/strings.es.json | 2 +- assets/strings.ja.json | 2 +- assets/strings.zh.json | 2 +- lib/ui/health/HealthHistoryPanel.dart | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 801d224e..8269f42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Button text for requesting vaccine and latest test [#680](https://github.com/rokwire/safer-illinois-app/issues/680). ## [2.10.33] - 2021-08-03 ### Added diff --git a/assets/strings.en.json b/assets/strings.en.json index 3dba4dc8..802abc47 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -626,7 +626,7 @@ "panel.health.covid19.history.label.verified": "Verified", "panel.health.covid19.history.label.verification_pending": "Verification Pending", "panel.health.covid19.history.message.clear_failed": "Failed to clear COVID-19 event history", - "panel.health.covid19.history.button.repost_history.title": "Request my latest test again", + "panel.health.covid19.history.button.repost_history.title": "Request my vaccine and latest test again", "panel.health.covid19.history.button.repost_history.hint": "", "panel.health.covid19.history.message.request_tests": "Your request has been submitted. You should receive your latest test within an hour", diff --git a/assets/strings.es.json b/assets/strings.es.json index de38287e..08e4b1ac 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -626,7 +626,7 @@ "panel.health.covid19.history.label.verified": "Verificado", "panel.health.covid19.history.label.verification_pending": "Verificación pendiente", "panel.health.covid19.history.message.clear_failed": "No se pudo borrar el historial de eventos de COVID-19", - "panel.health.covid19.history.button.repost_history.title": "Solicitar mi última prueba nuevamente", + "panel.health.covid19.history.button.repost_history.title": "Solicitar mi vacuna y la última prueba nuevamente", "panel.health.covid19.history.button.repost_history.hint": "", "panel.health.covid19.history.message.request_tests": "Su solicitud ha sido enviada. Debería recibir su última prueba en una hora", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index f685c753..e4cecc57 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -626,7 +626,7 @@ "panel.health.covid19.history.label.verified": "確認しました", "panel.health.covid19.history.label.verification_pending": "Verification Pending", "panel.health.covid19.history.message.clear_failed": "Failed to clear COVID-19 event history", - "panel.health.covid19.history.button.repost_history.title": "最新の検査結果を再度要求する", + "panel.health.covid19.history.button.repost_history.title": "ワクチンと最新の検査をもう一度リクエストしてください", "panel.health.covid19.history.button.repost_history.hint": "", "panel.health.covid19.history.message.request_tests": "要求は提出されました。1時間以内に最新の検査結果を受け取るはずです", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index 251b5122..1915e26e 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -626,7 +626,7 @@ "panel.health.covid19.history.label.verified": "已验证", "panel.health.covid19.history.label.verification_pending": "等待验证", "panel.health.covid19.history.message.clear_failed": "无法清除COVID-19事件历史记录", - "panel.health.covid19.history.button.repost_history.title": "再次请求我的最新测试", + "panel.health.covid19.history.button.repost_history.title": "再次請求我的疫苗和最新測試", "panel.health.covid19.history.button.repost_history.hint": "", "panel.health.covid19.history.message.request_tests": "您的请求已提.你应该在一小时内收到你的最新测验", diff --git a/lib/ui/health/HealthHistoryPanel.dart b/lib/ui/health/HealthHistoryPanel.dart index 2951d1db..ac745a14 100644 --- a/lib/ui/health/HealthHistoryPanel.dart +++ b/lib/ui/health/HealthHistoryPanel.dart @@ -205,7 +205,7 @@ class _HealthHistoryPanelState extends State implements Noti alignment: Alignment.center, children: [ ScalableRoundedButton( - label: Localization().getStringEx("panel.health.covid19.history.button.repost_history.title", "Request my latest test again"), + label: Localization().getStringEx("panel.health.covid19.history.button.repost_history.title", "Request my vaccine and latest test again"), hint: Localization().getStringEx("panel.health.covid19.history.button.repost_history.hint", ""), backgroundColor: Styles().colors.surface, fontSize: 16.0, From 03834b270f439bf4a4db0d9f3f97839b1924afa6 Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Wed, 4 Aug 2021 13:42:47 +0300 Subject: [PATCH 11/73] Version: 2.10.34+1034 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8269f42b..9a19557f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.34] - 2021-08-04 ### Changed - Button text for requesting vaccine and latest test [#680](https://github.com/rokwire/safer-illinois-app/issues/680). diff --git a/pubspec.yaml b/pubspec.yaml index 4c825867..b44d5e35 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.33+1033 +version: 2.10.34+1034 environment: sdk: ">=2.2.0 <3.0.0" From 58a49d3c183b9d0eb3fd9dc9b7de0e459991419b Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 08:51:49 +0300 Subject: [PATCH 12/73] Apply Health().userTestMonitorInterval to 'intervals' set, not in 'constants' when building display heath rules in DebugRules panel. --- lib/ui/debug/DebugHealthRulesPanel.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ui/debug/DebugHealthRulesPanel.dart b/lib/ui/debug/DebugHealthRulesPanel.dart index 4bec5770..4beb5fd4 100644 --- a/lib/ui/debug/DebugHealthRulesPanel.dart +++ b/lib/ui/debug/DebugHealthRulesPanel.dart @@ -85,12 +85,12 @@ class _DebugHealthRulesPanelState extends State{ Health().loadRulesJson(countyId: _selectedCountyId).then((Map rules) { if (mounted) { if ((rules != null) && (Health().userTestMonitorInterval != null)) { - dynamic constants = rules['constants']; - if (constants == null) { - rules['constants'] = constants = {}; + dynamic intervals = rules['intervals']; + if (intervals == null) { + rules['intervals'] = intervals = {}; } - if (constants is Map) { - constants[HealthRulesSet.UserTestMonitorInterval] = Health().userTestMonitorInterval; + if (intervals is Map) { + intervals[HealthRulesSet.UserTestMonitorInterval] = Health().userTestMonitorInterval; } } From 722464453d0b7c44ab2d25d27926bd2fb8f71b93 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 09:27:19 +0300 Subject: [PATCH 13/73] Define "vaccinated-force" status that handler "green" -4 status (#684). --- assets/health.rules.json | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index cb0e03d2..d823a841 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -119,7 +119,7 @@ "next_step_html": "positive.step.html" } }, - + "PCR.positive-IP": { "condition": "timeout", "params": { @@ -263,6 +263,14 @@ "fcm_topic": "vaccinated" }, + "vaccinated-force": { + "code": "green", + "priority": -4, + "next_step_html": "vaccinated.step.html", + "reason": "vaccinated.reason", + "fcm_topic": "vaccinated" + }, + "quarantine-on": { "code": "orange", "priority": 10, @@ -277,13 +285,7 @@ "interval": { "scope": "past" }, "vaccine": "effective" }, - "success": { - "code": "green", - "priority": -4, - "next_step_html": "vaccinated.step.html", - "reason": "vaccinated.reason", - "fcm_topic": "vaccinated" - }, + "success": "vaccinated-force", "fail": { "condition": "require-test", "params": { @@ -402,13 +404,7 @@ "interval": { "scope": "past" }, "vaccine": "effective" }, - "success": { - "code": "green", - "priority": -4, - "next_step_html": "vaccinated.step.html", - "reason": "exempt-off.green.reason", - "fcm_topic": "vaccinated" - }, + "success": "vaccinated-force", "fail": { "code": "orange", "priority": -1, @@ -416,20 +412,14 @@ "reason": "exempt-off.orange.reason" } }, - + "release": { "condition": "require-vaccine", "params": { "interval": { "scope": "past" }, "vaccine": "effective" }, - "success": { - "code": "green", - "priority": -4, - "next_step_html": "vaccinated.step.html", - "reason": "vaccinated.reason", - "fcm_topic": "vaccinated" - }, + "success": "vaccinated-force", "fail": { "code": "orange", "priority": -1, From ca2809e6863930f3e5a6a3567983f7534ab4c206 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 10:15:15 +0300 Subject: [PATCH 14/73] Disable vaccinated status if user has UserTestMonitorInterval defined (#684). --- assets/health.rules.json | 192 ++++++++++++++++++++++++--------------- 1 file changed, 121 insertions(+), 71 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index d823a841..149d04d2 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -94,67 +94,89 @@ "interval": { "min": 0, "max": 10, "scope": "future" } }, "success": { - "condition": "require-vaccine", + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": null, + "success": "PCR.positive.finish", "fail": { - "condition": "require-test", + "condition": "require-vaccine", "params": { - "interval": { "min": 11, "max": null, "scope": "future" } + "interval": { "scope": "past" }, + "vaccine": "effective" }, "success": null, - "fail": { - "code": "red", - "priority": 11, - "next_step": "test.now.step" - } + "fail": "PCR.positive.finish" } }, + "fail": "PCR.positive.quarantine" + }, + + "PCR.positive.finish": { + "condition": "require-test", + "params": { + "interval": { "min": 11, "max": null, "scope": "future" } + }, + "success": null, "fail": { "code": "red", "priority": 11, - "next_step_html": "positive.step.html" + "next_step": "test.now.step" } }, + "PCR.positive.quarantine": { + "code": "red", + "priority": 11, + "next_step_html": "positive.step.html" + }, + "PCR.positive-IP": { "condition": "timeout", "params": { "interval": { "min": 0, "max": 8, "scope": "future" } }, "success": { - "condition": "require-vaccine", + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": null, + "success": "PCR.positive-IP.finish", "fail": { - "condition": "require-test", + "condition": "require-vaccine", "params": { - "interval": { "min": 9, "max": null, "scope": "future" } + "interval": { "scope": "past" }, + "vaccine": "effective" }, "success": null, - "fail": { - "code": "red", - "priority": 11, - "next_step": "test.now.step", - "fcm_topic": "PCR.positive-IP" + "fail": "PCR.positive-IP.finish" } - } }, + "fail": "PCR.positive-IP.quarantine" + }, + + "PCR.positive-IP.finish": { + "condition": "require-test", + "params": { + "interval": { "min": 9, "max": null, "scope": "future" } + }, + "success": null, "fail": { "code": "red", "priority": 11, - "next_step_html": "positive-ip.step.html", - "event_explanation": "positive-ip.explanation", + "next_step": "test.now.step", "fcm_topic": "PCR.positive-IP" } }, + "PCR.positive-IP.quarantine": { + "code": "red", + "priority": 11, + "next_step_html": "positive-ip.step.html", + "event_explanation": "positive-ip.explanation", + "fcm_topic": "PCR.positive-IP" + }, + "PCR.positive-NIP.0": { "condition": "timeout", "params": { @@ -255,6 +277,15 @@ } }, + "vaccinated-handler": { + "condition": "test-interval", + "params": { + "interval": "UserTestMonitorInterval" + }, + "success": null, + "fail": "vaccinated" + }, + "vaccinated": { "code": "green", "priority": 4, @@ -280,30 +311,39 @@ }, "quarantine-off": { - "condition": "require-vaccine", + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", + "success": "quarantine-off.unvaccinated", "fail": { - "condition": "require-test", + "condition": "require-vaccine", "params": { - "interval": { "min": 0, "max": "TestMonitorInterval", "scope": "future", "current": true } + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": { - "code": "yellow", - "priority": -1, - "next_step": "test.resume.step", - "next_step_interval": "TestMonitorInterval", - "warning": "test.future.warning" - }, - "fail": { - "code": "orange", - "priority": -1, - "next_step": "test.now.step", - "reason": "test.now.reason" - } + "success": "vaccinated-force", + "fail": "quarantine-off.unvaccinated" + } + }, + + "quarantine-off.unvaccinated": { + "condition": "require-test", + "params": { + "interval": { "min": 0, "max": "TestMonitorInterval", "scope": "future", "current": true } + }, + "success": { + "code": "yellow", + "priority": -1, + "next_step": "test.resume.step", + "next_step_interval": "TestMonitorInterval", + "warning": "test.future.warning" + }, + "fail": { + "code": "orange", + "priority": -1, + "next_step": "test.now.step", + "reason": "test.now.reason" } }, @@ -371,15 +411,7 @@ "params": { "interval": { "min": 0, "max": "ExemptInterval", "scope": "future" } }, - "success": { - "condition": "require-vaccine", - "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" - }, - "success": "vaccinated", - "fail": "test-required" - }, + "success": null, "fail": { "code": "yellow", "priority": 12, @@ -399,36 +431,54 @@ }, "exempt-off": { - "condition": "require-vaccine", + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", + "success": "exempt-off.unvaccinated", "fail": { - "code": "orange", - "priority": -1, - "next_step": "test.now.step", - "reason": "exempt-off.orange.reason" + "condition": "require-vaccine", + "params": { + "interval": { "scope": "past" }, + "vaccine": "effective" + }, + "success": "vaccinated-force", + "fail": "exempt-off.unvaccinated" } }, + "exempt-off.unvaccinated": { + "code": "orange", + "priority": -1, + "next_step": "test.now.step", + "reason": "exempt-off.orange.reason" + }, + "release": { - "condition": "require-vaccine", + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", + "success": "release.unvaccinated", "fail": { - "code": "orange", - "priority": -1, - "next_step": "release.step", - "reason": "release.reason" + "condition": "require-vaccine", + "params": { + "interval": { "scope": "past" }, + "vaccine": "effective" + }, + "success": "vaccinated-force", + "fail": "release.unvaccinated" } } }, + "release.unvaccinated": { + "code": "orange", + "priority": -1, + "next_step": "release.step", + "reason": "release.reason" + }, + "tests" : { "rules": [ { @@ -814,7 +864,7 @@ "rules": [ { "vaccine": "effective", - "status": "vaccinated" + "status": "vaccinated-handler" } ] }, From c5a96262a5550fd837363f47ac8f28405b47655a Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 10:17:07 +0300 Subject: [PATCH 15/73] Updated CHANGELOG.md (#684). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a19557f..78eaf6a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Disable vaccinated status if user has UserTestMonitorInterval defined [#684](https://github.com/rokwire/safer-illinois-app/issues/684). ## [2.10.34] - 2021-08-04 ### Changed From 0f9726eb1d03e3fb0a191279bc1db70c9791fcdd Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 14:51:15 +0300 Subject: [PATCH 16/73] Added TMP debug shortcut (#684). --- lib/service/Health.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/service/Health.dart b/lib/service/Health.dart index eacad6ab..ab262f9b 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -695,6 +695,7 @@ class Health with Service implements NotificationsListener { } Future _loadUserTestMonitorInterval() async { +//TMP: return 8; if (this._isUserAuthenticated && (Config().healthUrl != null)) { String url = "${Config().healthUrl}/covid19/uin-override"; Response response = await Network().get(url, auth: Network.HealthUserAuth); @@ -1663,7 +1664,7 @@ class Health with Service implements NotificationsListener { // Current Server Time Future getServerTimeUtc() async { - //TMP: return DateTime.now().toUtc(); +//TMP: return DateTime.now().toUtc(); String url = (Config().healthUrl != null) ? "${Config().healthUrl}/covid19/time" : null; Response response = (url != null) ? await Network().get(url, auth: Network.AppAuth) : null; String responseBody = (response?.statusCode == 200) ? response.body : null; From 3b4495fd7f91cead8b241a244f5041d3d99a1f23 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 9 Aug 2021 14:52:07 +0300 Subject: [PATCH 17/73] Added appropriate next step, reason and event explanation in case vaccination is suspended (#684). --- assets/health.rules.json | 122 +++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index 149d04d2..6aea7e40 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -94,20 +94,20 @@ "interval": { "min": 0, "max": 10, "scope": "future" } }, "success": { - "condition": "test-interval", + "condition": "require-vaccine", "params": { - "interval": "UserTestMonitorInterval" + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": "PCR.positive.finish", - "fail": { - "condition": "require-vaccine", + "success": { + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": null, - "fail": "PCR.positive.finish" - } + "success": "PCR.positive.finish", + "fail": null + }, + "fail": "PCR.positive.finish" }, "fail": "PCR.positive.quarantine" }, @@ -137,20 +137,20 @@ "interval": { "min": 0, "max": 8, "scope": "future" } }, "success": { - "condition": "test-interval", + "condition": "require-vaccine", "params": { - "interval": "UserTestMonitorInterval" + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": "PCR.positive-IP.finish", - "fail": { - "condition": "require-vaccine", + "success": { + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": null, - "fail": "PCR.positive-IP.finish" - } + "success": "PCR.positive-IP.finish", + "fail": null + }, + "fail": "PCR.positive-IP.finish" }, "fail": "PCR.positive-IP.quarantine" }, @@ -282,7 +282,7 @@ "params": { "interval": "UserTestMonitorInterval" }, - "success": null, + "success": "vaccinated-suspended", "fail": "vaccinated" }, @@ -302,6 +302,14 @@ "fcm_topic": "vaccinated" }, + "vaccinated-suspended": { + "code": null, + "priority": null, + "next_step": "vaccinated.suspended.step", + "event_explanation": "vaccinated.suspended.explanation", + "reason": "vaccinated.suspended.reason" + }, + "quarantine-on": { "code": "orange", "priority": 10, @@ -311,20 +319,20 @@ }, "quarantine-off": { - "condition": "test-interval", + "condition": "require-vaccine", "params": { - "interval": "UserTestMonitorInterval" + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": "quarantine-off.unvaccinated", - "fail": { - "condition": "require-vaccine", + "success": { + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", - "fail": "quarantine-off.unvaccinated" - } + "success": "quarantine-off.unvaccinated", + "fail": "vaccinated-force" + }, + "fail": "quarantine-off.unvaccinated" }, "quarantine-off.unvaccinated": { @@ -431,20 +439,20 @@ }, "exempt-off": { - "condition": "test-interval", + "condition": "require-vaccine", "params": { - "interval": "UserTestMonitorInterval" + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": "exempt-off.unvaccinated", - "fail": { - "condition": "require-vaccine", + "success": { + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", - "fail": "exempt-off.unvaccinated" - } + "success": "exempt-off.unvaccinated", + "fail": "vaccinated-force" + }, + "fail": "exempt-off.unvaccinated" }, "exempt-off.unvaccinated": { @@ -455,20 +463,20 @@ }, "release": { - "condition": "test-interval", + "condition": "require-vaccine", "params": { - "interval": "UserTestMonitorInterval" + "interval": { "scope": "past" }, + "vaccine": "effective" }, - "success": "release.unvaccinated", - "fail": { - "condition": "require-vaccine", + "success": { + "condition": "test-interval", "params": { - "interval": { "scope": "past" }, - "vaccine": "effective" + "interval": "UserTestMonitorInterval" }, - "success": "vaccinated-force", - "fail": "release.unvaccinated" - } + "success": "release.unvaccinated", + "fail": "vaccinated-force" + }, + "fail": "release.unvaccinated" } }, @@ -895,6 +903,9 @@ "exposure.reason": "Your status changed to Orange because you received an exposure notification.", "vaccinated.step.html": "

We have a verified record of your completed COVID-19 vaccination on file.

Your vaccination status replaces testing for compliance and building access until further notice.

Please get an on-campus COVID-19 test if you experience symptoms.

Continue to monitor university communications for any changes to your testing policy.

", "vaccinated.reason": "Your status changed to Green because your vaccination is already effective.", + "vaccinated.suspended.step": "Get a test now.", + "vaccinated.suspended.explanation": "Nevertheless your COVID-19 vaccination you are required to resume testing for compliance and building access until further notice.", + "vaccinated.suspended.reason": "Nevertheless your status change to Orange because you are required to resume testing.", "quarantine-on.step": "Stay at home and avoid contacts", "quarantine-on.reason": "Your status changed to Orange because the Public Health department placed you in Quarantine.", "exempt-on.step": "You are exempted from testing", @@ -966,6 +977,9 @@ "exposure.reason": "Su estado cambió a Naranja porque recibió una notificación de exposición.", "vaccinated.step.html": "

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Su estado de vacunación reemplaza las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.

Hágase una prueba de COVID-19 en el campus si experimenta síntomas.

Continúe monitoreando las comunicaciones de la universidad para detectar cualquier cambio en su política de exámenes.

", "vaccinated.reason": "Su estado cambió a Verde porque su vacunación ya es efectiva.", + "vaccinated.suspended.step": "Hágase una prueba ahora.", + "vaccinated.suspended.explanation": "Sin embargo, su vacuna COVID-19 debe reanudar las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.", + "vaccinated.suspended.reason": "Sin embargo, su estado cambia a Orange porque debe reanudar las pruebas.", "quarantine-on.step": "Quédese en casa y evite los contactos", "quarantine-on.reason": "Su estado cambió a Orange porque el departamento de Salud Pública lo puso en cuarentena.", "exempt-on.step": "Estas exenta de pruebas", @@ -1037,6 +1051,9 @@ "exposure.reason": "您的狀態更改為橙色,因為您收到了曝光通知。", "vaccinated.step.html": "

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您的疫苗接種狀態將取代合規性測試和建立訪問權限,直至另行通知。

如果您出現症狀,請進行校園 COVID-19 測試。

繼續監控大學通訊,了解您的考試政策是否有任何變化。

", "vaccinated.reason": "您的狀態已更改為“綠色”,因為您的疫苗接種已經有效。", + "vaccinated.suspended.step": "立即進行測試。", + "vaccinated.suspended.explanation": "儘管如此,您需要重新進行 COVID-19 疫苗接種以確保合規性和建立訪問權限,直至另行通知。", + "vaccinated.suspended.reason": "儘管如此,您的狀態更改為橙色,因為您需要繼續測試。", "quarantine-on.step": "呆在家裡,避免接觸", "quarantine-on.reason": "您的狀態更改為“橙色”,因為公共衛生部門已將您隔離。", "exempt-on.step": "您免於測試", @@ -1108,6 +1125,9 @@ "exposure.reason": "接触通知を受信したため、ステータスがオレンジに変更されました。", "vaccinated.step.html": "

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたのワクチン接種状況は、追って通知があるまで、コンプライアンスと建物へのアクセスのテストに取って代わります。

症状が出た場合は、キャンパス内のCOVID-19検査を受けてください。

テストポリシーに変更がないか、大学のコミュニケーションを引き続き監視します。

", "vaccinated.reason": "予防接種がすでに有効になっているため、ステータスが緑に変わりました。", + "vaccinated.suspended.step": "今すぐテストを受けてください。", + "vaccinated.suspended.step.html": "それにもかかわらず、COVID-19ワクチン接種は、追って通知があるまで、コンプライアンスのテストとアクセスの構築を再開する必要があります。", + "vaccinated.suspended.reason": "それでも、テストを再開する必要があるため、ステータスはオレンジに変わります。", "quarantine-on.step": "家にいて、他の人との接触を避けてください。", "quarantine-on.reason": "Public Health departmentにより隔離状態に置かれているため、ステータスがオレンジに変更されました。", "exempt-on.step": "あなたはテストから免除されています", From 5c4695d531a1d56123b9345e166d525c0c95b4e3 Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Thu, 19 Aug 2021 13:05:51 +0300 Subject: [PATCH 18/73] Bug/issue 688 (#689) * Use while loops when processing exposures. Try to fix CME [#688] * Code formatting. * Use ConcurrentHashMap instead of HashMap in order to prevent CME [#688] * Update CHANGELOG.md [#688] --- CHANGELOG.md | 2 + .../covid/exposure/ExposurePlugin.java | 37 ++++++++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78eaf6a6..10697f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changed - Disable vaccinated status if user has UserTestMonitorInterval defined [#684](https://github.com/rokwire/safer-illinois-app/issues/684). +### Fixed +- Android CME crash when processing exposures [#688](https://github.com/rokwire/safer-illinois-app/issues/688). ## [2.10.34] - 2021-08-04 ### Changed diff --git a/android/app/src/main/java/edu/illinois/covid/exposure/ExposurePlugin.java b/android/app/src/main/java/edu/illinois/covid/exposure/ExposurePlugin.java index 9af5db1f..79005a1f 100644 --- a/android/app/src/main/java/edu/illinois/covid/exposure/ExposurePlugin.java +++ b/android/app/src/main/java/edu/illinois/covid/exposure/ExposurePlugin.java @@ -66,6 +66,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; import androidx.annotation.NonNull; import at.favre.lib.crypto.HKDF; @@ -161,8 +162,8 @@ public ExposurePlugin(MainActivity activity) { this.peripherals = new HashMap<>(); this.peripheralsRPIs = new HashMap<>(); - this.iosExposures = new HashMap<>(); - this.androidExposures = new HashMap<>(); + this.iosExposures = new ConcurrentHashMap<>(); + this.androidExposures = new ConcurrentHashMap<>(); this.i_TEK_map = loadTeksFromStorage(); this.peripherals_bg = new HashMap<>(); @@ -493,7 +494,9 @@ private void processExposures() { // 1. Collect all iOS expired records (not updated after exposureTimeoutIntervalInMillis) if ((iosExposures != null) && !iosExposures.isEmpty()) { - for (String peripheralAddress : iosExposures.keySet()) { + Iterator iosExposuresIterator = iosExposures.keySet().iterator(); + while (iosExposuresIterator.hasNext()) { + String peripheralAddress = iosExposuresIterator.next(); ExposureRecord record = iosExposures.get(peripheralAddress); if (record != null) { long lastHeardInterval = currentTimestamp - record.getTimestampUpdated(); @@ -503,10 +506,10 @@ private void processExposures() { expiredPeripheralAddress = new HashSet<>(); } expiredPeripheralAddress.add(peripheralAddress); - } else if(exposurePingIntervalInMillis <= lastHeardInterval) { + } else if (exposurePingIntervalInMillis <= lastHeardInterval) { Log.d(TAG, "ios exposure ping: " + peripheralAddress); BluetoothPeripheral peripheral = (peripherals != null) ? peripherals.get(peripheralAddress) : null; - if(peripheral != null) { + if (peripheral != null) { peripheral.readRemoteRssi(); } } @@ -517,8 +520,10 @@ private void processExposures() { if ((expiredPeripheralAddress != null) && !expiredPeripheralAddress.isEmpty()) { // Create copy so that to prevent crash with ConcurrentModificationException. Set expiredPeripheralAddressCopy = new HashSet<>(expiredPeripheralAddress); - for (String address : expiredPeripheralAddressCopy) { - // remove expired records from iosExposures + // remove expired records from iosExposures + Iterator expiredPeripheralIterator = expiredPeripheralAddressCopy.iterator(); + while (expiredPeripheralIterator.hasNext()) { + String address = expiredPeripheralIterator.next(); disconnectIosPeripheral(address); } } @@ -526,13 +531,15 @@ private void processExposures() { // 2. Collect all Android expired records (not updated after exposureTimeoutIntervalInMillis) Set expiredRPIs = null; if((androidExposures != null) && !androidExposures.isEmpty()) { - for(String encodedRpi : androidExposures.keySet()) { + Iterator androidExposuresIterator = androidExposures.keySet().iterator(); + while (androidExposuresIterator.hasNext()) { + String encodedRpi = androidExposuresIterator.next(); ExposureRecord record = androidExposures.get(encodedRpi); - if(record != null) { + if (record != null) { long lastHeardInterval = currentTimestamp - record.getTimestampUpdated(); - if(exposureTimeoutIntervalInMillis <= lastHeardInterval) { + if (exposureTimeoutIntervalInMillis <= lastHeardInterval) { Log.d(TAG, "Expired android exposure: " + encodedRpi); - if(expiredRPIs == null) { + if (expiredRPIs == null) { expiredRPIs = new HashSet<>(); } expiredRPIs.add(encodedRpi); @@ -545,7 +552,9 @@ private void processExposures() { // Create copy so that to prevent crash with ConcurrentModificationException. Set expiredRPIsCopy = new HashSet<>(expiredRPIs); // remove expired records from androidExposures - for (String encodedRpi : expiredRPIsCopy) { + Iterator expiredRPIsIterator = expiredRPIsCopy.iterator(); + while (expiredRPIsIterator.hasNext()) { + String encodedRpi = expiredRPIsIterator.next(); removeAndroidRpi(encodedRpi); } } @@ -553,13 +562,13 @@ private void processExposures() { private void clearExposures() { if ((iosExposures != null) && !iosExposures.isEmpty()) { - Map iosExposureCopy = new HashMap<>(iosExposures); + Map iosExposureCopy = new ConcurrentHashMap<>(iosExposures); for (String address : iosExposureCopy.keySet()) { disconnectIosPeripheral(address); } } if ((androidExposures != null) && !androidExposures.isEmpty()) { - Map androidExposureCopy = new HashMap<>(androidExposures); + Map androidExposureCopy = new ConcurrentHashMap<>(androidExposures); for (String encodedRpi : androidExposureCopy.keySet()) { removeAndroidRpi(encodedRpi); } From 12a67e5c8ac62695ab0da82cd05333281a6883f8 Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Thu, 19 Aug 2021 15:40:46 +0300 Subject: [PATCH 19/73] Bug/issue 690 (#691) * Android: Always call Service.startForeground() after Context.startForegroundService(). Fix for [#690] * Update CHANGELOG.md [#690] --- CHANGELOG.md | 1 + .../java/edu/illinois/covid/exposure/ble/ExposureClient.java | 5 +++++ .../edu/illinois/covid/exposure/ble/NotificationCreator.java | 1 + 3 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10697f86..6ecb0f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Disable vaccinated status if user has UserTestMonitorInterval defined [#684](https://github.com/rokwire/safer-illinois-app/issues/684). ### Fixed - Android CME crash when processing exposures [#688](https://github.com/rokwire/safer-illinois-app/issues/688). +- Android: RSE crash when scanning for exposures [#690](https://github.com/rokwire/safer-illinois-app/issues/690). ## [2.10.34] - 2021-08-04 ### Changed diff --git a/android/app/src/main/java/edu/illinois/covid/exposure/ble/ExposureClient.java b/android/app/src/main/java/edu/illinois/covid/exposure/ble/ExposureClient.java index 3310ae50..99035632 100644 --- a/android/app/src/main/java/edu/illinois/covid/exposure/ble/ExposureClient.java +++ b/android/app/src/main/java/edu/illinois/covid/exposure/ble/ExposureClient.java @@ -106,6 +106,11 @@ public void onCreate() { public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand - " + startId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // This call is required to be made after startForegroundService() since API 26 + startForegroundClientService(); + } + if (intent != null) { Bundle extras = intent.getExtras(); if (extras != null) { diff --git a/android/app/src/main/java/edu/illinois/covid/exposure/ble/NotificationCreator.java b/android/app/src/main/java/edu/illinois/covid/exposure/ble/NotificationCreator.java index 703f7382..d845ec4d 100644 --- a/android/app/src/main/java/edu/illinois/covid/exposure/ble/NotificationCreator.java +++ b/android/app/src/main/java/edu/illinois/covid/exposure/ble/NotificationCreator.java @@ -28,6 +28,7 @@ static Notification getNotification(Context context) { .setContentTitle(context.getString(R.string.exposure_notification_title)) .setSmallIcon(R.drawable.app_icon) .setContentIntent(pendingIntent) + .setOnlyAlertOnce(true) .setTicker(context.getString(R.string.exposure_notification_ticker)); notification = builder.build(); From 7d9acf71cf7c2d59aef73d5abdfa297b086b6353 Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Thu, 19 Aug 2021 15:47:02 +0300 Subject: [PATCH 20/73] Fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ecb0f13..b1f0d673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Disable vaccinated status if user has UserTestMonitorInterval defined [#684](https://github.com/rokwire/safer-illinois-app/issues/684). ### Fixed -- Android CME crash when processing exposures [#688](https://github.com/rokwire/safer-illinois-app/issues/688). +- Android: CME crash when processing exposures [#688](https://github.com/rokwire/safer-illinois-app/issues/688). - Android: RSE crash when scanning for exposures [#690](https://github.com/rokwire/safer-illinois-app/issues/690). ## [2.10.34] - 2021-08-04 From f45cf6582bd181c49042c2c705bf2737fc60388e Mon Sep 17 00:00:00 2001 From: Dobromir Dobrev Date: Thu, 19 Aug 2021 15:47:32 +0300 Subject: [PATCH 21/73] Version: 2.10.35+1035 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f0d673..b1fa8601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.35] - 2021-08-19 ### Changed - Disable vaccinated status if user has UserTestMonitorInterval defined [#684](https://github.com/rokwire/safer-illinois-app/issues/684). ### Fixed diff --git a/pubspec.yaml b/pubspec.yaml index b44d5e35..edf67aed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.34+1034 +version: 2.10.35+1035 environment: sdk: ">=2.2.0 <3.0.0" From 37991bdcecf45faacbe2d850ee7f9541a0770c79 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 30 Aug 2021 10:32:18 +0300 Subject: [PATCH 22/73] Updated next step text for vaccinated users that are forced to resume testing (#692). --- assets/health.rules.json | 47 ++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index 6aea7e40..e1d4de1a 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -242,6 +242,30 @@ }, "test-required": { + "condition": "require-vaccine", + "params": { + "interval": { "scope": "past" }, + "vaccine": "effective" + }, + "success": { + "condition": "test-interval", + "params": { + "interval": "UserTestMonitorInterval" + }, + "success": "test-required-vaccinated", + "fail": "test-required-default" + }, + "fail": "test-required-default" + }, + + "test-required-vaccinated": { + "code": "orange", + "priority": 1, + "next_step_html": "test.now.vaccinated.step.html", + "reason": "test.now.reason" + }, + + "test-required-default": { "code": "orange", "priority": 1, "next_step": "test.now.step", @@ -305,8 +329,7 @@ "vaccinated-suspended": { "code": null, "priority": null, - "next_step": "vaccinated.suspended.step", - "event_explanation": "vaccinated.suspended.explanation", + "next_step_html": "test.now.vaccinated.step.html", "reason": "vaccinated.suspended.reason" }, @@ -888,6 +911,7 @@ "test.monitor.step.html": "

Monitor your test results

The university encourages you to be vaccinated if you are able to do so. Visit vaccinefinder.org to find nearby appointments.

", "test.now.step": "Get a test now", "test.now.reason": "Your status changed to Orange because you are past due for a test.", + "test.now.vaccinated.step.html": "

Get a test now.

You have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time. Starting now, you are required to receive an on-campus COVID-19 test every other day (even if you are fully vaccinated and your vaccination record has been verified) to maintain compliance and have “Granted” Building Access status until cases improve and you are notified otherwise.

", "test.another.asap.step": "Get another test asap", "test.another.now.step.html": "

Get your second test now.

  • Limit yourself to essential activities until you get the second negative result.
  • Your building access will change to Granted (Yellow) with the second negative test result.

See testing schedule and rules.

", "test.after.step.html": "

Get your second test after {next_step_date}. You must take two on-campus tests by Jan. 25.

  • Separate the tests by three days of quarantine: if the first test is on day one, the second test will be on day five.
  • Limit yourself to essential activities until you get the second negative result.
  • Your building access will change to Granted (Yellow) with the second negative test result.

See testing schedule and rules.

", @@ -903,9 +927,7 @@ "exposure.reason": "Your status changed to Orange because you received an exposure notification.", "vaccinated.step.html": "

We have a verified record of your completed COVID-19 vaccination on file.

Your vaccination status replaces testing for compliance and building access until further notice.

Please get an on-campus COVID-19 test if you experience symptoms.

Continue to monitor university communications for any changes to your testing policy.

", "vaccinated.reason": "Your status changed to Green because your vaccination is already effective.", - "vaccinated.suspended.step": "Get a test now.", - "vaccinated.suspended.explanation": "Nevertheless your COVID-19 vaccination you are required to resume testing for compliance and building access until further notice.", - "vaccinated.suspended.reason": "Nevertheless your status change to Orange because you are required to resume testing.", + "vaccinated.suspended.reason": "Your status change to Orange because you have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time.", "quarantine-on.step": "Stay at home and avoid contacts", "quarantine-on.reason": "Your status changed to Orange because the Public Health department placed you in Quarantine.", "exempt-on.step": "You are exempted from testing", @@ -962,6 +984,7 @@ "test.monitor.step.html": "

Controle los resultados de su prueba

La universidad le anima a vacunarse si puede hacerlo. Visite vacunafinder.org para encontrar citas cercanas.

", "test.now.step": "Haz una prueba ahora", "test.now.reason": "Su estado cambió a Naranja porque está atrasado en un examen.", + "test.now.vaccinated.step.html": "

Hágase una prueba ahora.

Se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo. A partir de ahora, debe recibir una prueba de COVID-19 en el campus cada dos días (incluso si está completamente vacunado y su registro de vacunación ha sido verificado) para mantener el cumplimiento y tener el estado de Acceso al edificio “Otorgado” hasta que los casos mejoren y usted se notifica de otra manera.

", "test.another.asap.step": "Obtenga otra prueba lo antes posible", "test.another.now.step.html": "

Obtenga su segunda prueba ahora. Debes de tomar dos pruebas en el campus antes del 25 de enero.

  • Separe las pruebas con tres días de cuarentena:  si la primera prueba es el día uno, la segunda prueba será el día cinco.
  • Limítese a las actividades esenciales hasta que obtenga el segundo resultado negativo.
  • El acceso al edificio cambiará a Concedido (amarillo) con el segundo resultado negativo de la prueba.

Ver el calendario y las reglas de las pruebas.

", "test.after.step.html": "

Obtenga su segunda prueba después del {next_step_date}. Debes de tomar dos pruebas en el campus antes del 25 de enero.

  • Separe las pruebas con tres días de cuarentena:  si la primera prueba es el día uno, la segunda prueba será el día cinco.
  • Limítese a las actividades esenciales hasta que obtenga el segundo resultado negativo.
  • El acceso al edificio cambiará a Concedido (amarillo) con el segundo resultado negativo de la prueba.

Ver el calendario y las reglas de las pruebas.

", @@ -977,9 +1000,7 @@ "exposure.reason": "Su estado cambió a Naranja porque recibió una notificación de exposición.", "vaccinated.step.html": "

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Su estado de vacunación reemplaza las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.

Hágase una prueba de COVID-19 en el campus si experimenta síntomas.

Continúe monitoreando las comunicaciones de la universidad para detectar cualquier cambio en su política de exámenes.

", "vaccinated.reason": "Su estado cambió a Verde porque su vacunación ya es efectiva.", - "vaccinated.suspended.step": "Hágase una prueba ahora.", - "vaccinated.suspended.explanation": "Sin embargo, su vacuna COVID-19 debe reanudar las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.", - "vaccinated.suspended.reason": "Sin embargo, su estado cambia a Orange porque debe reanudar las pruebas.", + "vaccinated.suspended.reason": "Su estado cambia a Naranja porque se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo.", "quarantine-on.step": "Quédese en casa y evite los contactos", "quarantine-on.reason": "Su estado cambió a Orange porque el departamento de Salud Pública lo puso en cuarentena.", "exempt-on.step": "Estas exenta de pruebas", @@ -1036,6 +1057,7 @@ "test.monitor.step.html": "

監控您的測試結果

如果您有能力,大學鼓勵您接種疫苗。 訪問 vaccinefinder.org 查找附近的約會。

", "test.now.step": "立即獲得測試", "test.now.reason": "您的狀態更改為“橙色”,因為您已逾期進行測試。", + "test.now.vaccinated.step.html": "

立即進行測試。

您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。 從現在開始,您必須每隔一天接受一次校內 COVID-19 測試(即使您已完全接種疫苗並且您的疫苗接種記錄已得到驗證)以保持合規性並具有“授予”建築物訪問權限狀態,直到病例有所改善並且您 另行通知。

", "test.another.asap.step": "盡快獲得另一個測試", "test.another.now.step.html": "

现在进行第二次测试. 你必须在1月25日之前检测两次.

  • 两次测试之间需隔离三天:如果第一次测试在第一天,第二次测试将在第五天
  • 在得到第二个阴性测试结果之前,尽量只做必要的出行
  • 在第二个阴性结果出来后,您的建筑访问权限将更改为“已授予”(黄色)

见测试计划和规则.

", "test.after.step.html": "

在{next_step_date}之后进行第二次测试. 你必须在1月25日之前检测两次.

  • 两次测试之间需隔离三天:如果第一次测试在第一天,第二次测试将在第五天
  • 在得到第二个阴性测试结果之前,尽量只做必要的出行
  • 在第二个阴性结果出来后,您的建筑访问权限将更改为“已授予”(黄色)

见测试计划和规则.

", @@ -1051,9 +1073,7 @@ "exposure.reason": "您的狀態更改為橙色,因為您收到了曝光通知。", "vaccinated.step.html": "

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您的疫苗接種狀態將取代合規性測試和建立訪問權限,直至另行通知。

如果您出現症狀,請進行校園 COVID-19 測試。

繼續監控大學通訊,了解您的考試政策是否有任何變化。

", "vaccinated.reason": "您的狀態已更改為“綠色”,因為您的疫苗接種已經有效。", - "vaccinated.suspended.step": "立即進行測試。", - "vaccinated.suspended.explanation": "儘管如此,您需要重新進行 COVID-19 疫苗接種以確保合規性和建立訪問權限,直至另行通知。", - "vaccinated.suspended.reason": "儘管如此,您的狀態更改為橙色,因為您需要繼續測試。", + "vaccinated.suspended.reason": "您的狀態更改為橙色,因為您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。", "quarantine-on.step": "呆在家裡,避免接觸", "quarantine-on.reason": "您的狀態更改為“橙色”,因為公共衛生部門已將您隔離。", "exempt-on.step": "您免於測試", @@ -1110,6 +1130,7 @@ "test.monitor.step.html": "

検査結果をモニタリングする

大学は、可能であれば予防接種を受けることを推奨しています。 vaccinefinder.org にアクセスして、近くの予定を見つけてください。

", "test.now.step": "今検査を受ける", "test.now.reason": "検査期日が過ぎたため、ステータスがオレンジに変更されました。", + "test.now.vaccinated.step.html": "

今すぐテストを受けてください。

あなたは、短期間に陽性のCOVID-19症例が大幅に増加している地域にいると特定されました。 今から、コンプライアンスを維持し、ケースが改善してあなたが それ以外の場合は通知されます。

", "test.another.asap.step": "今すぐ別の検査を受ける", "test.another.now.step.html": "

2回目の検査をいま受ける。

  • 2回目の陰性結果を受け取るまでは必要な行動だけに制限してください。
  • 2回目の陰性結果とともに建物に入る許可が「GRANTED」(黄)に変更されます。

で検査予定とルールについての情報を確認してください。.

", "test.after.step.html": "

2回目の検査を{next_step_date}の後で受ける。 1月25日までにキャンプの検査を2回受ける必要があります。

  • 2つの検査の間に、3日間の隔離期間を設けて下さい。: 1回目の検査を初日に受けた場合、2回目の検査は5日目になります。
  • 2回目の陰性結果を受けるまでには必要な行動だけに制限してください。
  • 2回目の陰性結果とともに建物に入る許可が「GRANTED」(黄)に変更されます。

で検査予定とルールについての情報を確認してください。.

", @@ -1125,9 +1146,7 @@ "exposure.reason": "接触通知を受信したため、ステータスがオレンジに変更されました。", "vaccinated.step.html": "

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたのワクチン接種状況は、追って通知があるまで、コンプライアンスと建物へのアクセスのテストに取って代わります。

症状が出た場合は、キャンパス内のCOVID-19検査を受けてください。

テストポリシーに変更がないか、大学のコミュニケーションを引き続き監視します。

", "vaccinated.reason": "予防接種がすでに有効になっているため、ステータスが緑に変わりました。", - "vaccinated.suspended.step": "今すぐテストを受けてください。", - "vaccinated.suspended.step.html": "それにもかかわらず、COVID-19ワクチン接種は、追って通知があるまで、コンプライアンスのテストとアクセスの構築を再開する必要があります。", - "vaccinated.suspended.reason": "それでも、テストを再開する必要があるため、ステータスはオレンジに変わります。", + "vaccinated.suspended.reason": "短期間に陽性のCOVID-19症例が大幅に増加している地域にいることが確認されたため、ステータスがオレンジに変わります。", "quarantine-on.step": "家にいて、他の人との接触を避けてください。", "quarantine-on.reason": "Public Health departmentにより隔離状態に置かれているため、ステータスがオレンジに変更されました。", "exempt-on.step": "あなたはテストから免除されています", From b15d70e8e9b9be705aed2fb45e489b99e0026913 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 30 Aug 2021 10:35:32 +0300 Subject: [PATCH 23/73] Updated CHANGELOG.md (#692). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1fa8601..2a7d4b38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Updated next step text for vaccinated users that are forced to resume testing [#692](https://github.com/rokwire/safer-illinois-app/issues/692). ## [2.10.35] - 2021-08-19 ### Changed From dd55aa66e3e951ad80dfd8442a58aa45846be588 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 1 Sep 2021 12:11:57 +0300 Subject: [PATCH 24/73] Added vaccination widget in Home panel (#696). --- assets/flexUI.json | 2 +- lib/ui/health/HealthHomePanel.dart | 157 ++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 4 deletions(-) diff --git a/assets/flexUI.json b/assets/flexUI.json index cec2ab5f..7d22bf38 100644 --- a/assets/flexUI.json +++ b/assets/flexUI.json @@ -7,7 +7,7 @@ "home": ["connect", "stay_healthy", "your_health"], "home.connect": ["netid", "phone"], - "home.stay_healthy" : ["recent_event", "next_step", "symptom_checkin", "add_test_result"], + "home.stay_healthy" : ["vaccination", "recent_event", "next_step", "symptom_checkin", "add_test_result"], "home.your_health" : [ "health_status", "tiles", "health_history", "find_test_location", "wellness_center", "_groups", "switch_account"], "home.your_health.tiles": ["county_guidelines", "care_team"], diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 1e0a89d6..06577fcb 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -265,6 +265,8 @@ class _HealthHomePanelState extends State implements Notificati contentList.add(_buildSymptomCheckInSection()); } else if (code == 'add_test_result') { contentList.add(_buildAddTestResultSection()); + } else if (code == 'vaccination') { + contentList.add(_buildVaccinationSection()); } } @@ -543,7 +545,7 @@ class _HealthHomePanelState extends State implements Notificati borderColor: Styles().colors.fillColorSecondary, backgroundColor: Styles().colors.surface, textColor: Styles().colors.fillColorPrimary, - onTap: ()=> _onTapFindLocations(), + onTap: ()=> _onTapFindTestLocations(), )), ) ],), @@ -678,6 +680,146 @@ class _HealthHomePanelState extends State implements Notificati )); } + Widget _buildVaccinationSection() { + + HealthHistory lastVaccineTaken; + int numberOfTakenVaccines = 0; + + for (HealthHistory historyEntry in Health().history ?? []) { + if (historyEntry.isVaccine) { + if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineEffective?.toLowerCase()) { + // 5.2.4 When effective then hide the widget + return null; + } + else if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineTaken?.toLowerCase()) { + numberOfTakenVaccines++; + if (lastVaccineTaken == null) { + lastVaccineTaken = historyEntry; + } + } + } + } + + String headingTitle = 'VACCINATION', headingDate; + String statusTitleText, statusTitleHtml; + String statusDescriptionText, statusDescriptionHtml; + + if (lastVaccineTaken == null) { + statusTitleText = 'Get a vaccine now'; + statusDescriptionText = """ +• COVID-19 vaccines are safe. +• COVID-19 vaccines are effective. +• Once you are fully vaccinated, you can start doing more. +• COVID-19 vaccination is a safer way to help build protection. +• None of the COVID-19 vaccines can make you sick with COVID-19."""; + } + else if (numberOfTakenVaccines == 1) { + headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; + + DateTime nextDoseDate = lastVaccineTaken?.dateUtc?.add(Duration(days: 21)); + String nextDoseDateStr = AppDateTime.formatDateTime(nextDoseDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; + + statusTitleText = 'Get your second vaccination'; + statusDescriptionText = "Get your second dose of vaccine on $nextDoseDateStr."; + } + else { + headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; + + DateTime vaccineEffectiveDate = lastVaccineTaken?.dateUtc?.add(Duration(days: 14)); + String vaccineEffectiveDateStr = AppDateTime.formatDateTime(vaccineEffectiveDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; + + statusTitleText = 'Wait for vaccination to get effective'; + statusDescriptionText = "Your vaccination will become effective after $vaccineEffectiveDateStr."; + } + + List contentWidgets = [ + Row(children: [ + Text(headingTitle ?? '', style: TextStyle(letterSpacing: 0.5, fontFamily: Styles().fontFamilies.bold, fontSize: 12, color: Styles().colors.fillColorPrimary),), + Expanded(child: Container(),), + Text(headingDate ?? '', style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 12, color: Styles().colors.textSurface),) + ],), + ]; + + if (AppString.isStringNotEmpty(statusTitleText)) { + contentWidgets.addAll([ + Container(height: 12,), + Text(statusTitleText, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary),), + ]); + } + + if (AppString.isStringNotEmpty(statusTitleHtml)) { + contentWidgets.addAll([ + Container(height: 12,), + Html(data: statusTitleHtml, onLinkTap: (url) => _onTapLink(url), + style: { + "body": Style(fontFamily: Styles().fontFamilies.medium, fontSize: FontSize(16), color: Styles().colors.fillColorPrimary, padding: EdgeInsets.zero, margin: EdgeInsets.zero) + }, + ), + ]); + } + + if (AppString.isStringNotEmpty(statusDescriptionText)) { + contentWidgets.addAll([ + Container(height: 12,), + Text(statusDescriptionText, style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Styles().colors.fillColorPrimary),), + ]); + } + + if (AppString.isStringNotEmpty(statusDescriptionHtml)) { + contentWidgets.addAll([ + Container(height: 12,), + Html(data: statusDescriptionHtml, onLinkTap: (url) => _onTapLink(url), + style: { + "body": Style(fontFamily: Styles().fontFamilies.medium, fontSize: FontSize(16), color: Styles().colors.fillColorPrimary, padding: EdgeInsets.zero, margin: EdgeInsets.zero) + }, + ), + ]); + } + + List contentList = [ + Stack(children: [ + Visibility(visible: true, + child: Padding(padding: EdgeInsets.symmetric(horizontal: 16), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: contentWidgets), + ), + ), + Visibility(visible: (_isRefreshing == true), + child: Container( + height: 80, + child: Align(alignment: Alignment.center, + child: SizedBox(height: 24, width: 24, + child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), ) + ), + ), + ), + ), + ],), + ]; + + if (numberOfTakenVaccines < 2) { + contentList.addAll([ + Container(margin: EdgeInsets.only(top: 14, bottom: 14), height: 1, color: Styles().colors.fillColorPrimaryTransparent015,), + + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Semantics(explicitChildNodes: true, child: ScalableRoundedButton( + label: "Make vaccination appointment", + hint: "", + borderColor: Styles().colors.fillColorSecondary, + backgroundColor: Styles().colors.surface, + textColor: Styles().colors.fillColorPrimary, + onTap: _onTapFindVaccineAppointment, + )), + ) + ]); + } + + return Semantics(container: true, child: + Container(padding: EdgeInsets.symmetric(vertical: 16), decoration: BoxDecoration(color: Styles().colors.surface, borderRadius: BorderRadius.all(Radius.circular(4)), boxShadow: [BoxShadow(color: Styles().colors.blackTransparent018, spreadRadius: 2.0, blurRadius: 6.0, offset: Offset(2, 2))] ), child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: contentList), + )); + } + Widget _buildTileButtons() { List contentList = []; @@ -774,7 +916,7 @@ class _HealthHomePanelState extends State implements Notificati hint: Localization().getStringEx("panel.covid19home.button.find_test_locations.hint", ""), borderRadius: BorderRadius.circular(4), height: null, - onTap: ()=>_onTapFindLocations(), + onTap: ()=>_onTapFindTestLocations(), ), ); } @@ -929,7 +1071,7 @@ class _HealthHomePanelState extends State implements Notificati } } - void _onTapFindLocations() { + void _onTapFindTestLocations() { if (Connectivity().isNotOffline) { Analytics.instance.logSelect(target: "COVID-19 Find Test Locations"); Navigator.push(context, CupertinoPageRoute(builder: (context) => HealthTestLocationsPanel())); @@ -938,6 +1080,15 @@ class _HealthHomePanelState extends State implements Notificati } } + void _onTapFindVaccineAppointment() { + if (Connectivity().isNotOffline) { + Analytics.instance.logSelect(target: "COVID-19 Find Vaccine Appointment"); + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://mymckinley.illinois.edu'))); + } else { + AppAlert.showOfflineMessage(context); + } + } + void _onTapGroups() { if (Connectivity().isNotOffline) { Analytics.instance.logSelect(target: "COVID-19 Groups"); From c291ae06693dfeeb5066148a2cc1d0513a0a98cf Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 1 Sep 2021 12:13:07 +0300 Subject: [PATCH 25/73] Updated CHANGELOG.md (#696). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7d4b38..ff8f3d2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changed - Updated next step text for vaccinated users that are forced to resume testing [#692](https://github.com/rokwire/safer-illinois-app/issues/692). +### Added +- Added vaccination widget in Home panel [#696](https://github.com/rokwire/safer-illinois-app/issues/696). ## [2.10.35] - 2021-08-19 ### Changed From 2db88664659b530032aa4f87a9f82a1d1ab00adc Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 1 Sep 2021 17:11:43 +0300 Subject: [PATCH 26/73] version: 2.10.36+1036 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8f3d2c..235d4a98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.36] - 2021-09-01 ### Changed - Updated next step text for vaccinated users that are forced to resume testing [#692](https://github.com/rokwire/safer-illinois-app/issues/692). ### Added diff --git a/pubspec.yaml b/pubspec.yaml index edf67aed..8f18958a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.35+1035 +version: 2.10.36+1036 environment: sdk: ">=2.2.0 <3.0.0" From 0394515f3e7f5efcc76343df53a795211d0219c0 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 2 Sep 2021 09:42:25 +0300 Subject: [PATCH 27/73] (1) "Your vaccine is already effective." => "Your COVID-19 vaccination has been verified." (#698) --- assets/strings.en.json | 2 +- assets/strings.es.json | 2 +- assets/strings.ja.json | 2 +- assets/strings.zh.json | 2 +- lib/ui/health/HealthStatusUpdatePanel.dart | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/strings.en.json b/assets/strings.en.json index 802abc47..94930620 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -708,7 +708,7 @@ "panel.health.status_update.label.reason.symptoms.title": "You reported new symptoms", "panel.health.status_update.label.reason.exposed.title": "You were exposed to someone who was likely infected", "panel.health.status_update.label.reason.exposure.detail": "Duration of exposure: ", - "panel.health.status_update.label.reason.vaccine.effective.title": "Your vaccine is already effective.", + "panel.health.status_update.label.reason.vaccine.effective.title": "Your COVID-19 vaccination has been verified.", "panel.health.status_update.label.reason.vaccine.taken.title": "Your vaccine is taken.", "panel.health.status_update.label.reason.vaccine.title": "Your vaccine is applied.", "panel.health.status_update.label.reason.action.title": "Health authorities require you to take an action.", diff --git a/assets/strings.es.json b/assets/strings.es.json index 08e4b1ac..070786d3 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -708,7 +708,7 @@ "panel.health.status_update.label.reason.symptoms.title": "Reportaste nuevos síntomas", "panel.health.status_update.label.reason.exposed.title": "Estuvo expuesto a alguien que probablemente estaba infectado", "panel.health.status_update.label.reason.exposure.detail": "Duración de exposición:", - "panel.health.status_update.label.reason.vaccine.effective.title": "Tu vacuna ya es eficaz.", + "panel.health.status_update.label.reason.vaccine.effective.title": "Se ha verificado su vacuna COVID-19.", "panel.health.status_update.label.reason.vaccine.taken.title": "Se toma su vacuna.", "panel.health.status_update.label.reason.vaccine.title": "Se aplica su vacuna.", "panel.health.status_update.label.reason.action.title": "Las autoridades sanitarias le exigen que actúe.", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index e4cecc57..f8f94d23 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -708,7 +708,7 @@ "panel.health.status_update.label.reason.symptoms.title": "You reported new symptoms", "panel.health.status_update.label.reason.exposed.title": "You were exposed to someone who was likely infected", "panel.health.status_update.label.reason.exposure.detail": "Duration of exposure: ", - "panel.health.status_update.label.reason.vaccine.effective.title": "Your vaccine is already effective.", + "panel.health.status_update.label.reason.vaccine.effective.title": "Your COVID-19 vaccination has been verified.", "panel.health.status_update.label.reason.vaccine.taken.title": "Your vaccine is taken.", "panel.health.status_update.label.reason.vaccine.title": "Your vaccine is applied.", "panel.health.status_update.label.reason.action.title": "Health authorities require you to take an action.", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index 1915e26e..e9918651 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -708,7 +708,7 @@ "panel.health.status_update.label.reason.symptoms.title": "你报告了新的症状", "panel.health.status_update.label.reason.exposed.title": "你接触过可能被感染的人", "panel.health.status_update.label.reason.exposure.detail": "暴露时间: ", - "panel.health.status_update.label.reason.vaccine.effective.title": "您的疫苗已經有效。", + "panel.health.status_update.label.reason.vaccine.effective.title": "您的 COVID-19 疫苗接種已通過驗證。", "panel.health.status_update.label.reason.vaccine.taken.title": "您的疫苗已經服用。", "panel.health.status_update.label.reason.vaccine.title": "您的疫苗已接種。", "panel.health.status_update.label.reason.action.title": "衛生當局要求您採取行動。", diff --git a/lib/ui/health/HealthStatusUpdatePanel.dart b/lib/ui/health/HealthStatusUpdatePanel.dart index 6579ab20..7f8f8891 100644 --- a/lib/ui/health/HealthStatusUpdatePanel.dart +++ b/lib/ui/health/HealthStatusUpdatePanel.dart @@ -208,7 +208,7 @@ class _HealthStatusUpdatePanelState extends State { } else if (reasonHistory.isVaccine) { if (reasonHistory.isVaccineEffective) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.effective.title", "Your vaccine is already effective."); + reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.effective.title", "Your COVID-19 vaccination has been verified."); } else if (reasonHistory.isVaccineTaken) { reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.taken.title", "Your vaccine is taken."); From 2e104666fcc42c4989f1dc95d2959886253a5ebc Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 2 Sep 2021 09:47:57 +0300 Subject: [PATCH 28/73] (2) "Your status changed to Green because your vaccination is already effective." => "We have a verified record of your completed COVID-19 vaccination on file; your status has changed to Green." (#698). --- assets/health.rules.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index e1d4de1a..e6c70a72 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -926,7 +926,7 @@ "exposure.step.html": "

You have likely been exposed to a person who is infected with COVID-19.

  • You must quarantine yourself immediately.
  • Stay home. Do not go to work, school, or public areas.
  • Separate yourself from others in your home.
  • Contact covidwellness@illinois.edu for guidance.
  • Get tested after {next_step_date} to see if you have developed the disease.
  • More Info: Quarantine and Isolation
", "exposure.reason": "Your status changed to Orange because you received an exposure notification.", "vaccinated.step.html": "

We have a verified record of your completed COVID-19 vaccination on file.

Your vaccination status replaces testing for compliance and building access until further notice.

Please get an on-campus COVID-19 test if you experience symptoms.

Continue to monitor university communications for any changes to your testing policy.

", - "vaccinated.reason": "Your status changed to Green because your vaccination is already effective.", + "vaccinated.reason": "We have a verified record of your completed COVID-19 vaccination on file; your status has changed to Green.", "vaccinated.suspended.reason": "Your status change to Orange because you have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time.", "quarantine-on.step": "Stay at home and avoid contacts", "quarantine-on.reason": "Your status changed to Orange because the Public Health department placed you in Quarantine.", @@ -999,7 +999,7 @@ "exposure.step.html": "

Es probable que haya estado expuesto a una persona infectada con COVID-19.

  • Debe ponerse en cuarentena inmediatamente.
  • Quedarse en casa. No vaya al trabajo, la escuela o áreas públicas.
  • Sepárate de las demás en tu casa.
  • Póngase en contacto con covidwellness@illinois.edu para obtener orientación.
  • Hágase la prueba después del {next_step_date} para ver si ha desarrollado la enfermedad.
  • Más información: Cuarentena y aislamiento
", "exposure.reason": "Su estado cambió a Naranja porque recibió una notificación de exposición.", "vaccinated.step.html": "

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Su estado de vacunación reemplaza las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.

Hágase una prueba de COVID-19 en el campus si experimenta síntomas.

Continúe monitoreando las comunicaciones de la universidad para detectar cualquier cambio en su política de exámenes.

", - "vaccinated.reason": "Su estado cambió a Verde porque su vacunación ya es efectiva.", + "vaccinated.reason": "Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo; su estado ha cambiado a verde.", "vaccinated.suspended.reason": "Su estado cambia a Naranja porque se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo.", "quarantine-on.step": "Quédese en casa y evite los contactos", "quarantine-on.reason": "Su estado cambió a Orange porque el departamento de Salud Pública lo puso en cuarentena.", @@ -1072,7 +1072,7 @@ "exposure.step.html": "

您可能已經接觸了感染了COVID-19的人。

  • 您必須立即隔離自己。
  • 您已被強制隔離。
  • 與家中的其他人分開。
  • 請與 covidwellness@illinois.edu 聯繫以獲取指導。
  • 在{next_step_date}之後進行測試,看看您是否已患上這種疾病。
  • 更多信息: 隔離與隔離
", "exposure.reason": "您的狀態更改為橙色,因為您收到了曝光通知。", "vaccinated.step.html": "

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您的疫苗接種狀態將取代合規性測試和建立訪問權限,直至另行通知。

如果您出現症狀,請進行校園 COVID-19 測試。

繼續監控大學通訊,了解您的考試政策是否有任何變化。

", - "vaccinated.reason": "您的狀態已更改為“綠色”,因為您的疫苗接種已經有效。", + "vaccinated.reason": "我們有您完成的 COVID-19 疫苗接種的經過驗證的記錄存檔; 您的狀態已更改為綠色。", "vaccinated.suspended.reason": "您的狀態更改為橙色,因為您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。", "quarantine-on.step": "呆在家裡,避免接觸", "quarantine-on.reason": "您的狀態更改為“橙色”,因為公共衛生部門已將您隔離。", @@ -1145,7 +1145,7 @@ "exposure.step.html": "

COVID-19に感染している人と接触した可能性があります。

  • 直ちに隔離してください。
  • 家にいてください。仕事や学校や公共の場へ行かないでください。
  • 家にいる人から離れてください。
  • ガイダンスを受けるにはcovidwellness@illinois.eduに連絡してください。
  • {next_step_date}の後で検査を受けて、自分が発症しているかどうかを確認してください。
  • より詳しい情報: 隔離と孤立
", "exposure.reason": "接触通知を受信したため、ステータスがオレンジに変更されました。", "vaccinated.step.html": "

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたのワクチン接種状況は、追って通知があるまで、コンプライアンスと建物へのアクセスのテストに取って代わります。

症状が出た場合は、キャンパス内のCOVID-19検査を受けてください。

テストポリシーに変更がないか、大学のコミュニケーションを引き続き監視します。

", - "vaccinated.reason": "予防接種がすでに有効になっているため、ステータスが緑に変わりました。", + "vaccinated.reason": "完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。 ステータスが緑に変わりました。", "vaccinated.suspended.reason": "短期間に陽性のCOVID-19症例が大幅に増加している地域にいることが確認されたため、ステータスがオレンジに変わります。", "quarantine-on.step": "家にいて、他の人との接触を避けてください。", "quarantine-on.reason": "Public Health departmentにより隔離状態に置かれているため、ステータスがオレンジに変更されました。", From 41902918235145230dcb1e938d326cffa0c71b1b Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 2 Sep 2021 09:49:19 +0300 Subject: [PATCH 29/73] Updated CHANGELOG.md (#698). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 235d4a98..284347af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Updated strings for vaccination effective status change [#698](https://github.com/rokwire/safer-illinois-app/issues/698). ## [2.10.36] - 2021-09-01 ### Changed From 85782d022d7b8b54d79c58c938bffe75938f4bcb Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 09:14:12 +0300 Subject: [PATCH 30/73] Updated "test.monitor.step.html" for vaccinated users (#700) --- assets/health.rules.json | 82 ++++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/assets/health.rules.json b/assets/health.rules.json index e6c70a72..87d43855 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -231,14 +231,41 @@ "params": { "interval": { "min": 0, "max": "TestMonitorInterval", "max-weekdays-extent": "TestMonitorWeekdaysExtent", "scope": "future", "current": true } }, + "success": "test-fulfilled", + "fail": "test-required" + }, + + "test-fulfilled": { + "condition": "require-vaccine", + "params": { + "interval": { "scope": "past" }, + "vaccine": "effective" + }, "success": { - "code": "yellow", - "priority": 1, - "next_step_html": "test.monitor.step.html", - "next_step_interval": "TestMonitorInterval", - "warning": "test.future.warning" + "condition": "test-interval", + "params": { + "interval": "UserTestMonitorInterval" + }, + "success": "test-fulfilled-vaccinated", + "fail": "test-fulfilled-default" }, - "fail": "test-required" + "fail": "test-fulfilled-default" + }, + + "test-fulfilled-vaccinated": { + "code": "yellow", + "priority": 1, + "next_step_html": "test.monitor.vaccinated.step.html", + "next_step_interval": "TestMonitorInterval", + "warning": "test.future.warning" + }, + + "test-fulfilled-default": { + "code": "yellow", + "priority": 1, + "next_step_html": "test.monitor.step.html", + "next_step_interval": "TestMonitorInterval", + "warning": "test.future.warning" }, "test-required": { @@ -422,14 +449,41 @@ "params": { "interval": { "min": -1, "max": 1, "current": true } }, + "success": "out-of-test-compliance-fulfilled", + "fail": "test-required" + }, + + "out-of-test-compliance-fulfilled": { + "condition": "require-vaccine", + "params": { + "interval": { "scope": "past" }, + "vaccine": "effective" + }, "success": { - "code": null, - "priority": 1, - "next_step_html": "test.monitor.step.html", - "next_step_interval": 1, - "warning": "test.future.warning" + "condition": "test-interval", + "params": { + "interval": "UserTestMonitorInterval" + }, + "success": "out-of-test-compliance-fulfilled-vaccinated", + "fail": "out-of-test-compliance-fulfilled-default" }, - "fail": "test-required" + "fail": "out-of-test-compliance-fulfilled-default" + }, + + "out-of-test-compliance-fulfilled-vaccinated": { + "code": null, + "priority": 1, + "next_step_html": "test.monitor.vaccinated.step.html", + "next_step_interval": 1, + "warning": "test.future.warning" + }, + + "out-of-test-compliance-fulfilled-default": { + "code": null, + "priority": 1, + "next_step_html": "test.monitor.step.html", + "next_step_interval": 1, + "warning": "test.future.warning" }, "exempt-on": { @@ -909,6 +963,7 @@ "positive-nip.step.html": "

You are not required to self-isolate unless you have COVID-like symptoms and are directed to do so by your licensed health professional.

", "positive-nip.explanation": "Your Saliva PCR test shows the VIRUS IS DETECTED in your REPEAT POSITIVE at a NON-INFECTIOUS LEVEL.", "test.monitor.step.html": "

Monitor your test results

The university encourages you to be vaccinated if you are able to do so. Visit vaccinefinder.org to find nearby appointments.

", + "test.monitor.vaccinated.step.html": "

Monitor your test results

We have a verified record of your completed COVID-19 vaccination on file.

You have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time. You are required to receive an on-campus COVID-19 test to maintain compliance and have “Granted” Building Access status until cases improve and you are notified otherwise.

", "test.now.step": "Get a test now", "test.now.reason": "Your status changed to Orange because you are past due for a test.", "test.now.vaccinated.step.html": "

Get a test now.

You have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time. Starting now, you are required to receive an on-campus COVID-19 test every other day (even if you are fully vaccinated and your vaccination record has been verified) to maintain compliance and have “Granted” Building Access status until cases improve and you are notified otherwise.

", @@ -982,6 +1037,7 @@ "positive-nip.step.html": "

No es necesario que se aísle a sí mismo a menos que tenga síntomas similares a los de COVID y su profesional de la salud autorizado le indique que lo haga.

", "positive-nip.explanation": "Su prueba de PCR de saliva muestra que el VIRUS ESTÁ DETECTADO en su REPETICIÓN POSITIVA en un NIVEL NO INFECCIOSO.", "test.monitor.step.html": "

Controle los resultados de su prueba

La universidad le anima a vacunarse si puede hacerlo. Visite vacunafinder.org para encontrar citas cercanas.

", + "test.monitor.vaccinated.step.html": "

Controle los resultados de su prueba

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo. Debe recibir una prueba COVID-19 en el campus para mantener el cumplimiento y tener el estado de Acceso al edificio “Concedido” hasta que los casos mejoren y se le notifique lo contrario.

", "test.now.step": "Haz una prueba ahora", "test.now.reason": "Su estado cambió a Naranja porque está atrasado en un examen.", "test.now.vaccinated.step.html": "

Hágase una prueba ahora.

Se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo. A partir de ahora, debe recibir una prueba de COVID-19 en el campus cada dos días (incluso si está completamente vacunado y su registro de vacunación ha sido verificado) para mantener el cumplimiento y tener el estado de Acceso al edificio “Otorgado” hasta que los casos mejoren y usted se notifica de otra manera.

", @@ -1055,6 +1111,7 @@ "positive-nip.step.html": "

除非您有類似COVID的症狀並且由您的有執照的衛生專業人員指示這樣做,否則您無需自我隔離。

", "positive-nip.explanation": "您的唾液PCR測試顯示病毒在非陽性水平的重複陽性中被檢測到。", "test.monitor.step.html": "

監控您的測試結果

如果您有能力,大學鼓勵您接種疫苗。 訪問 vaccinefinder.org 查找附近的約會。

", + "test.monitor.vaccinated.step.html": "

監控您的測試結果

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。 您需要接受校內 COVID-19 測試以保持合規性並具有“授予”建築物訪問權限狀態,直到情況有所改善並且您收到其他通知。

", "test.now.step": "立即獲得測試", "test.now.reason": "您的狀態更改為“橙色”,因為您已逾期進行測試。", "test.now.vaccinated.step.html": "

立即進行測試。

您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。 從現在開始,您必須每隔一天接受一次校內 COVID-19 測試(即使您已完全接種疫苗並且您的疫苗接種記錄已得到驗證)以保持合規性並具有“授予”建築物訪問權限狀態,直到病例有所改善並且您 另行通知。

", @@ -1128,6 +1185,7 @@ "positive-nip.step.html": "
認可された医療従事者から指示された場合以外は自己隔離する必要やキャンパスで行う検査を60日間受ける必要はありません。Safer Illinoisアプリは、この期間にアクセスを許可するように設定されています。質問や困ったことがある場合は、covidwellness@illinois.edu まで連絡してください。
", "positive-nip.explanation": "唾液PCR検査は、不感染性レベルにある場合、2回目の検査結果が陽性検査を出す可能性があります。", "test.monitor.step.html": "

検査結果をモニタリングする

大学は、可能であれば予防接種を受けることを推奨しています。 vaccinefinder.org にアクセスして、近くの予定を見つけてください。

", + "test.monitor.vaccinated.step.html": "

検査結果をモニタリングする

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたは、短期間に陽性のCOVID-19症例が大幅に増加している地域にいると特定されました。 コンプライアンスを維持するためにキャンパス内のCOVID-19テストを受け、ケースが改善されて別の方法で通知されるまで「許可された」建物アクセスステータスを取得する必要があります。

", "test.now.step": "今検査を受ける", "test.now.reason": "検査期日が過ぎたため、ステータスがオレンジに変更されました。", "test.now.vaccinated.step.html": "

今すぐテストを受けてください。

あなたは、短期間に陽性のCOVID-19症例が大幅に増加している地域にいると特定されました。 今から、コンプライアンスを維持し、ケースが改善してあなたが それ以外の場合は通知されます。

", From 5e66f58a42ab98b56d99243809f04ad68bf6fb90 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 09:16:00 +0300 Subject: [PATCH 31/73] Updated CHANGELOG.md (#700). --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 284347af..fb3ba90c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Changed - Updated strings for vaccination effective status change [#698](https://github.com/rokwire/safer-illinois-app/issues/698). +- Updated strings for next step for vaccinated users in UIN override list who has fulfilled their tests [#700](https://github.com/rokwire/safer-illinois-app/issues/700). ## [2.10.36] - 2021-09-01 ### Changed From 5ebfd13c382abf5add5510840af5c9f2b2ec2bb0 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 09:53:08 +0300 Subject: [PATCH 32/73] Added _RefreshOptions.toString() getter for logging purpose. --- lib/service/Health.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/service/Health.dart b/lib/service/Health.dart index ab262f9b..0b394bbd 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -262,6 +262,8 @@ class Health with Service implements NotificationsListener { } Future _refreshInternal(_RefreshOptions options) async { + //Log.d("Health._refreshInternal($options)"); + _refreshOptions = options; NotificationService().notify(notifyRefreshing); @@ -1917,6 +1919,19 @@ class _RefreshOptions { _RefreshOptions difference(_RefreshOptions other) { return _RefreshOptions.fromSet(options?.difference(other?.options)); } + + String toString() { + String list = ''; + for (_RefreshOption option in _RefreshOption.values) { + if (options.contains(option)) { + if (list.isNotEmpty) { + list += ', '; + } + list += option.toString(); + } + } + return '[$list]'; + } } enum _RefreshOption { From 7fbb9b7da08dbd1c421ab84549485517b6af8d6d Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 10:29:06 +0300 Subject: [PATCH 33/73] warning grouped with next step in HealthStatusBlob (#702). --- lib/model/Health.dart | 45 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 37e95bd0..65584d84 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -126,12 +126,12 @@ class HealthStatusBlob { final String nextStepHtml; final DateTime nextStepDateUtc; - final String eventExplanation; - final String eventExplanationHtml; - final String warning; final String warningHtml; + final String eventExplanation; + final String eventExplanationHtml; + final String reason; final dynamic fcmTopic; @@ -141,7 +141,12 @@ class HealthStatusBlob { static const String _nextStepDateMacro = '{next_step_date}'; static const String _nextStepDateFormat = 'EEEE, MMM d'; - HealthStatusBlob({this.code, this.priority, this.nextStep, this.nextStepHtml, this.nextStepDateUtc, this.eventExplanation, this.eventExplanationHtml, this.warning, this.warningHtml, this.reason, this.fcmTopic, this.historyBlob}); + HealthStatusBlob({this.code, this.priority, + this.nextStep, this.nextStepHtml, this.nextStepDateUtc, + this.warning, this.warningHtml, + this.eventExplanation, this.eventExplanationHtml, + this.reason, + this.fcmTopic, this.historyBlob}); factory HealthStatusBlob.fromJson(Map json) { return (json != null) ? HealthStatusBlob( @@ -150,10 +155,10 @@ class HealthStatusBlob { nextStep: json['next_step'], nextStepHtml: json['next_step_html'], nextStepDateUtc: healthDateTimeFromString(json['next_step_date']), - eventExplanation: json['event_explanation'], - eventExplanationHtml: json['event_explanation_html'], warning: json['warning'], warningHtml: json['warning_html'], + eventExplanation: json['event_explanation'], + eventExplanationHtml: json['event_explanation_html'], reason: json['reason'], fcmTopic: json['fcm_topic'], historyBlob: HealthHistoryBlob.fromJson(json['history_blob']), @@ -167,10 +172,10 @@ class HealthStatusBlob { 'next_step': nextStep, 'next_step_html': nextStepHtml, 'next_step_date': healthDateTimeToString(nextStepDateUtc), - 'event_explanation': eventExplanation, - 'event_explanation_html': eventExplanationHtml, 'warning': warning, 'warning_html': warningHtml, + 'event_explanation': eventExplanation, + 'event_explanation_html': eventExplanationHtml, 'reason': reason, 'fcm_topic': fcmTopic, 'history_blob': historyBlob?.toJson(), @@ -184,10 +189,10 @@ class HealthStatusBlob { (o.nextStep == nextStep) && (o.nextStepHtml == nextStepHtml) && (o.nextStepDateUtc == nextStepDateUtc) && - (o.eventExplanation == eventExplanation) && - (o.eventExplanationHtml == eventExplanationHtml) && (o.warning == warning) && (o.warningHtml == warningHtml) && + (o.eventExplanation == eventExplanation) && + (o.eventExplanationHtml == eventExplanationHtml) && (o.reason == reason) && DeepCollectionEquality().equals(o.fcmTopic, fcmTopic) && (o.historyBlob == historyBlob); @@ -199,10 +204,10 @@ class HealthStatusBlob { (nextStep?.hashCode ?? 0) ^ (nextStepHtml?.hashCode ?? 0) ^ (nextStepDateUtc?.hashCode ?? 0) ^ - (eventExplanation?.hashCode ?? 0) ^ - (eventExplanationHtml?.hashCode ?? 0) ^ (warning?.hashCode ?? 0) ^ (warningHtml?.hashCode ?? 0) ^ + (eventExplanation?.hashCode ?? 0) ^ + (eventExplanationHtml?.hashCode ?? 0) ^ (reason?.hashCode ?? 0) ^ (DeepCollectionEquality().hash(fcmTopic) ?? 0) ^ (historyBlob?.hashCode ?? 0); @@ -231,14 +236,6 @@ class HealthStatusBlob { return null; } - String get displayEventExplanation { - return _processMacros(eventExplanation); - } - - String get displayEventExplanationHtml { - return _processMacros(eventExplanationHtml); - } - String get displayWarning { return _processMacros(warning); } @@ -247,6 +244,14 @@ class HealthStatusBlob { return _processMacros(warningHtml); } + String get displayEventExplanation { + return _processMacros(eventExplanation); + } + + String get displayEventExplanationHtml { + return _processMacros(eventExplanationHtml); + } + String get displayReason { return _processMacros(reason); } From 703b608340d17290040d3f109dd87d5b2d076d63 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 10:33:24 +0300 Subject: [PATCH 34/73] warning grouped with next step in HealthRuleStatus (#702). --- lib/model/Health.dart | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 65584d84..95a9177a 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -3390,20 +3390,22 @@ class HealthRuleStatus extends _HealthRuleStatus { final _HealthRuleInterval nextStepInterval; final DateTime nextStepDateUtc; - final dynamic eventExplanation; - final dynamic eventExplanationHtml; - final dynamic warning; final dynamic warningHtml; + final dynamic eventExplanation; + final dynamic eventExplanationHtml; + final dynamic reason; final dynamic fcmTopic; HealthRuleStatus({this.code, this.priority, this.nextStep, this.nextStepHtml, this.nextStepInterval, this.nextStepDateUtc, + this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, - this.warning, this.warningHtml, this.reason, this.fcmTopic }); + this.reason, + this.fcmTopic }); factory HealthRuleStatus.fromJson(Map json) { return (json != null) ? HealthRuleStatus( @@ -3412,10 +3414,10 @@ class HealthRuleStatus extends _HealthRuleStatus { nextStep: json['next_step'], nextStepHtml: json['next_step_html'], nextStepInterval: _HealthRuleInterval.fromJson(json['next_step_interval']), - eventExplanation: json['event_explanation'], - eventExplanationHtml: json['event_explanation_html'], warning: json['warning'], warningHtml: json['warning_html'], + eventExplanation: json['event_explanation'], + eventExplanationHtml: json['event_explanation_html'], reason: json['reason'], fcmTopic: json['fcm_topic'] ) : null; @@ -3430,10 +3432,10 @@ class HealthRuleStatus extends _HealthRuleStatus { nextStepHtml: status.nextStepHtml, nextStepInterval: status.nextStepInterval, nextStepDateUtc: nextStepDateUtc ?? status.nextStepDateUtc, - eventExplanation: status.eventExplanation, - eventExplanationHtml: status.eventExplanationHtml, warning: status.warning, warningHtml: status.warningHtml, + eventExplanation: status.eventExplanation, + eventExplanationHtml: status.eventExplanationHtml, reason: status.reason, fcmTopic: status.fcmTopic, ) : null; @@ -3449,12 +3451,12 @@ class HealthRuleStatus extends _HealthRuleStatus { (o.nextStepInterval == nextStepInterval) && (o.nextStepDateUtc == nextStepDateUtc) && - (o.eventExplanation == eventExplanation) && - (o.eventExplanationHtml == eventExplanationHtml) && - (o.warning == warning) && (o.warningHtml == warningHtml) && + (o.eventExplanation == eventExplanation) && + (o.eventExplanationHtml == eventExplanationHtml) && + (o.reason == reason) && (o.fcmTopic == fcmTopic); @@ -3468,12 +3470,12 @@ class HealthRuleStatus extends _HealthRuleStatus { (nextStepInterval?.hashCode ?? 0) ^ (nextStepDateUtc?.hashCode ?? 0) ^ - (eventExplanation?.hashCode ?? 0) ^ - (eventExplanationHtml?.hashCode ?? 0) ^ - (warning?.hashCode ?? 0) ^ (warningHtml?.hashCode ?? 0) ^ + (eventExplanation?.hashCode ?? 0) ^ + (eventExplanationHtml?.hashCode ?? 0) ^ + (reason?.hashCode ?? 0) ^ (fcmTopic?.hashCode ?? 0); From a3ffc1a3c25f5f8d3e5a4236c4a1858423939a85 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 10:42:49 +0300 Subject: [PATCH 35/73] 'reason' field in HealthStatusBlob and HealthRuleStatus renamed to 'statusUpdateReason' (#702). --- lib/model/Health.dart | 28 +++++++++++----------- lib/service/Health.dart | 4 ++-- lib/ui/health/HealthStatusUpdatePanel.dart | 8 +++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 95a9177a..c665e00a 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -132,7 +132,7 @@ class HealthStatusBlob { final String eventExplanation; final String eventExplanationHtml; - final String reason; + final String statusUpdateReason; final dynamic fcmTopic; @@ -145,7 +145,7 @@ class HealthStatusBlob { this.nextStep, this.nextStepHtml, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, - this.reason, + this.statusUpdateReason, this.fcmTopic, this.historyBlob}); factory HealthStatusBlob.fromJson(Map json) { @@ -159,7 +159,7 @@ class HealthStatusBlob { warningHtml: json['warning_html'], eventExplanation: json['event_explanation'], eventExplanationHtml: json['event_explanation_html'], - reason: json['reason'], + statusUpdateReason: json['status_update_reason'] ?? json['reason'], fcmTopic: json['fcm_topic'], historyBlob: HealthHistoryBlob.fromJson(json['history_blob']), ) : null; @@ -176,7 +176,7 @@ class HealthStatusBlob { 'warning_html': warningHtml, 'event_explanation': eventExplanation, 'event_explanation_html': eventExplanationHtml, - 'reason': reason, + 'status_update_reason': statusUpdateReason, 'fcm_topic': fcmTopic, 'history_blob': historyBlob?.toJson(), }; @@ -193,7 +193,7 @@ class HealthStatusBlob { (o.warningHtml == warningHtml) && (o.eventExplanation == eventExplanation) && (o.eventExplanationHtml == eventExplanationHtml) && - (o.reason == reason) && + (o.statusUpdateReason == statusUpdateReason) && DeepCollectionEquality().equals(o.fcmTopic, fcmTopic) && (o.historyBlob == historyBlob); } @@ -208,7 +208,7 @@ class HealthStatusBlob { (warningHtml?.hashCode ?? 0) ^ (eventExplanation?.hashCode ?? 0) ^ (eventExplanationHtml?.hashCode ?? 0) ^ - (reason?.hashCode ?? 0) ^ + (statusUpdateReason?.hashCode ?? 0) ^ (DeepCollectionEquality().hash(fcmTopic) ?? 0) ^ (historyBlob?.hashCode ?? 0); @@ -252,8 +252,8 @@ class HealthStatusBlob { return _processMacros(eventExplanationHtml); } - String get displayReason { - return _processMacros(reason); + String get displayStatusUpdateReason { + return _processMacros(statusUpdateReason); } String _processMacros(String value) { @@ -3396,7 +3396,7 @@ class HealthRuleStatus extends _HealthRuleStatus { final dynamic eventExplanation; final dynamic eventExplanationHtml; - final dynamic reason; + final dynamic statusUpdateReason; final dynamic fcmTopic; @@ -3404,7 +3404,7 @@ class HealthRuleStatus extends _HealthRuleStatus { this.nextStep, this.nextStepHtml, this.nextStepInterval, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, - this.reason, + this.statusUpdateReason, this.fcmTopic }); factory HealthRuleStatus.fromJson(Map json) { @@ -3418,7 +3418,7 @@ class HealthRuleStatus extends _HealthRuleStatus { warningHtml: json['warning_html'], eventExplanation: json['event_explanation'], eventExplanationHtml: json['event_explanation_html'], - reason: json['reason'], + statusUpdateReason: json['status_update_reason'] ?? json['reason'], fcmTopic: json['fcm_topic'] ) : null; } @@ -3436,7 +3436,7 @@ class HealthRuleStatus extends _HealthRuleStatus { warningHtml: status.warningHtml, eventExplanation: status.eventExplanation, eventExplanationHtml: status.eventExplanationHtml, - reason: status.reason, + statusUpdateReason: status.statusUpdateReason, fcmTopic: status.fcmTopic, ) : null; } @@ -3457,7 +3457,7 @@ class HealthRuleStatus extends _HealthRuleStatus { (o.eventExplanation == eventExplanation) && (o.eventExplanationHtml == eventExplanationHtml) && - (o.reason == reason) && + (o.statusUpdateReason == statusUpdateReason) && (o.fcmTopic == fcmTopic); @@ -3476,7 +3476,7 @@ class HealthRuleStatus extends _HealthRuleStatus { (eventExplanation?.hashCode ?? 0) ^ (eventExplanationHtml?.hashCode ?? 0) ^ - (reason?.hashCode ?? 0) ^ + (statusUpdateReason?.hashCode ?? 0) ^ (fcmTopic?.hashCode ?? 0); diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 0b394bbd..59bd09b8 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -839,7 +839,7 @@ class Health with Service implements NotificationsListener { eventExplanationHtml: rules.localeString(defaultStatus.eventExplanationHtml), warning: rules.localeString(defaultStatus.warning), warningHtml: rules.localeString(defaultStatus.warningHtml), - reason: rules.localeString(defaultStatus.reason), + statusUpdateReason: rules.localeString(defaultStatus.statusUpdateReason), fcmTopic: defaultStatus.fcmTopic, historyBlob: null, ), @@ -887,7 +887,7 @@ class Health with Service implements NotificationsListener { eventExplanationHtml: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanationHtml) : status.blob.eventExplanationHtml, warning: ((ruleStatus.warning != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warning) : status.blob.warning, warningHtml: ((ruleStatus.warningHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warningHtml) : status.blob.warningHtml, - reason: ((ruleStatus.reason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.reason) : status.blob.reason, + statusUpdateReason: ((ruleStatus.statusUpdateReason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReason) : status.blob.statusUpdateReason, fcmTopic: ((ruleStatus.fcmTopic != null) || (ruleStatus.code != null)) ? ruleStatus.fcmTopic : status.blob.fcmTopic, historyBlob: historyEntry.blob, ), diff --git a/lib/ui/health/HealthStatusUpdatePanel.dart b/lib/ui/health/HealthStatusUpdatePanel.dart index 7f8f8891..f978a5f2 100644 --- a/lib/ui/health/HealthStatusUpdatePanel.dart +++ b/lib/ui/health/HealthStatusUpdatePanel.dart @@ -162,7 +162,7 @@ class _HealthStatusUpdatePanelState extends State { Widget _buildReasonContent(){ String date = AppDateTime.formatDateTime(widget.status?.dateUtc?.toLocal(), format: "MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode); - String reasonStatusText = widget.status?.blob?.displayReason; + String reasonText = widget.status?.blob?.displayStatusUpdateReason; HealthHistoryBlob reasonHistory = widget.status?.blob?.historyBlob; String reasonHistoryName; @@ -231,7 +231,7 @@ class _HealthStatusUpdatePanelState extends State { } } - if ((reasonStatusText != null) || (reasonHistoryName != null) || (reasonHistoryDetail != null)) { + if ((reasonText != null) || (reasonHistoryName != null) || (reasonHistoryDetail != null)) { List content = [ Container(height: 30,), Text(Localization().getStringEx("panel.health.status_update.label.reason.title", "STATUS CHANGED BECAUSE:"), textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold),), @@ -258,10 +258,10 @@ class _HealthStatusUpdatePanelState extends State { ]); } - if (reasonStatusText != null) { + if (reasonText != null) { content.addAll([ Container(height: 60,), - Text(reasonStatusText, textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 14, fontFamily: Styles().fontFamilies.bold),), + Text(reasonText, textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 14, fontFamily: Styles().fontFamilies.bold),), Container(height: 30,), ]); } From d1eac973dfb4e2a1d004c50e6973a03ad4dc7a91 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 11:01:03 +0300 Subject: [PATCH 36/73] Created HealthStatusBlob.fromRuleStatus constructor that enhances creation of HealthStatusBlob (#702). --- lib/model/Health.dart | 17 +++++++++++++++++ lib/service/Health.dart | 32 ++++++-------------------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index c665e00a..f1926056 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -212,6 +212,23 @@ class HealthStatusBlob { (DeepCollectionEquality().hash(fcmTopic) ?? 0) ^ (historyBlob?.hashCode ?? 0); + factory HealthStatusBlob.fromRuleStatus(HealthRuleStatus ruleStatus, { HealthRulesSet rules, HealthStatusBlob previousStatusBlob, HealthHistoryBlob historyBlob }) { + return (ruleStatus != null) ? HealthStatusBlob( + code: (ruleStatus.code != null) ? ruleStatus.code : previousStatusBlob?.code, + priority: (ruleStatus.priority != null) ? ruleStatus.priority.abs() : previousStatusBlob?.priority, + nextStep: ((ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.nextStep) : previousStatusBlob?.nextStep, + nextStepHtml: ((ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.nextStepHtml) : previousStatusBlob?.nextStepHtml, + nextStepDateUtc: ((ruleStatus.nextStepInterval != null) || (ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? ruleStatus.nextStepDateUtc : previousStatusBlob?.nextStepDateUtc, + eventExplanation: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanation) : previousStatusBlob?.eventExplanation, + eventExplanationHtml: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanationHtml) : previousStatusBlob?.eventExplanationHtml, + warning: ((ruleStatus.warning != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warning) : previousStatusBlob?.warning, + warningHtml: ((ruleStatus.warningHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warningHtml) : previousStatusBlob?.warningHtml, + statusUpdateReason: ((ruleStatus.statusUpdateReason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReason) : previousStatusBlob?.statusUpdateReason, + fcmTopic: ((ruleStatus.fcmTopic != null) || (ruleStatus.code != null)) ? ruleStatus.fcmTopic : previousStatusBlob?.fcmTopic, + historyBlob: historyBlob, + ) : null; + } + String get displayNextStep { return _processMacros(nextStep); } diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 59bd09b8..5fb1aa2b 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -829,20 +829,9 @@ class Health with Service implements NotificationsListener { HealthStatus status = HealthStatus( dateUtc: null, - blob: HealthStatusBlob( - code: defaultStatus.code, - priority: defaultStatus.priority, - nextStep: rules.localeString(defaultStatus.nextStep), - nextStepHtml: rules.localeString(defaultStatus.nextStepHtml), - nextStepDateUtc: null, - eventExplanation: rules.localeString(defaultStatus.eventExplanation), - eventExplanationHtml: rules.localeString(defaultStatus.eventExplanationHtml), - warning: rules.localeString(defaultStatus.warning), - warningHtml: rules.localeString(defaultStatus.warningHtml), - statusUpdateReason: rules.localeString(defaultStatus.statusUpdateReason), - fcmTopic: defaultStatus.fcmTopic, - historyBlob: null, - ), + blob: HealthStatusBlob.fromRuleStatus(defaultStatus, + rules: rules, + ) ); // Start from older @@ -877,18 +866,9 @@ class Health with Service implements NotificationsListener { if ((ruleStatus != null) && ruleStatus.canUpdateStatus(blob: status.blob)) { status = HealthStatus( dateUtc: historyEntry.dateUtc, - blob: HealthStatusBlob( - code: (ruleStatus.code != null) ? ruleStatus.code : status.blob.code, - priority: (ruleStatus.priority != null) ? ruleStatus.priority.abs() : status.blob.priority, - nextStep: ((ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.nextStep) : status.blob.nextStep, - nextStepHtml: ((ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.nextStepHtml) : status.blob.nextStepHtml, - nextStepDateUtc: ((ruleStatus.nextStepInterval != null) || (ruleStatus.nextStep != null) || (ruleStatus.nextStepHtml != null) || (ruleStatus.code != null)) ? ruleStatus.nextStepDateUtc : status.blob.nextStepDateUtc, - eventExplanation: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanation) : status.blob.eventExplanation, - eventExplanationHtml: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanationHtml) : status.blob.eventExplanationHtml, - warning: ((ruleStatus.warning != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warning) : status.blob.warning, - warningHtml: ((ruleStatus.warningHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warningHtml) : status.blob.warningHtml, - statusUpdateReason: ((ruleStatus.statusUpdateReason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReason) : status.blob.statusUpdateReason, - fcmTopic: ((ruleStatus.fcmTopic != null) || (ruleStatus.code != null)) ? ruleStatus.fcmTopic : status.blob.fcmTopic, + blob: HealthStatusBlob.fromRuleStatus(ruleStatus, + rules: rules, + previousStatusBlob: status.blob, historyBlob: historyEntry.blob, ), ); From 8d55fbd8554dbdd046a1c6d33bc97e915481ca5c Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 11:37:31 +0300 Subject: [PATCH 37/73] Added statusUpdateReasonHtml to health status blob (#702). --- lib/model/Health.dart | 65 +++++++++++++--------- lib/service/Health.dart | 6 +- lib/ui/health/HealthStatusUpdatePanel.dart | 49 +++++++++++++--- 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index f1926056..423453fd 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -133,6 +133,7 @@ class HealthStatusBlob { final String eventExplanationHtml; final String statusUpdateReason; + final String statusUpdateReasonHtml; final dynamic fcmTopic; @@ -145,7 +146,7 @@ class HealthStatusBlob { this.nextStep, this.nextStepHtml, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, - this.statusUpdateReason, + this.statusUpdateReason, this.statusUpdateReasonHtml, this.fcmTopic, this.historyBlob}); factory HealthStatusBlob.fromJson(Map json) { @@ -160,6 +161,7 @@ class HealthStatusBlob { eventExplanation: json['event_explanation'], eventExplanationHtml: json['event_explanation_html'], statusUpdateReason: json['status_update_reason'] ?? json['reason'], + statusUpdateReasonHtml: json['status_update_reason_html'] ?? json['reason_html'], fcmTopic: json['fcm_topic'], historyBlob: HealthHistoryBlob.fromJson(json['history_blob']), ) : null; @@ -177,6 +179,7 @@ class HealthStatusBlob { 'event_explanation': eventExplanation, 'event_explanation_html': eventExplanationHtml, 'status_update_reason': statusUpdateReason, + 'status_update_reason_html': statusUpdateReasonHtml, 'fcm_topic': fcmTopic, 'history_blob': historyBlob?.toJson(), }; @@ -194,6 +197,7 @@ class HealthStatusBlob { (o.eventExplanation == eventExplanation) && (o.eventExplanationHtml == eventExplanationHtml) && (o.statusUpdateReason == statusUpdateReason) && + (o.statusUpdateReasonHtml == statusUpdateReasonHtml) && DeepCollectionEquality().equals(o.fcmTopic, fcmTopic) && (o.historyBlob == historyBlob); } @@ -209,6 +213,7 @@ class HealthStatusBlob { (eventExplanation?.hashCode ?? 0) ^ (eventExplanationHtml?.hashCode ?? 0) ^ (statusUpdateReason?.hashCode ?? 0) ^ + (statusUpdateReasonHtml?.hashCode ?? 0) ^ (DeepCollectionEquality().hash(fcmTopic) ?? 0) ^ (historyBlob?.hashCode ?? 0); @@ -224,6 +229,7 @@ class HealthStatusBlob { warning: ((ruleStatus.warning != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warning) : previousStatusBlob?.warning, warningHtml: ((ruleStatus.warningHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warningHtml) : previousStatusBlob?.warningHtml, statusUpdateReason: ((ruleStatus.statusUpdateReason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReason) : previousStatusBlob?.statusUpdateReason, + statusUpdateReasonHtml: ((ruleStatus.statusUpdateReasonHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReasonHtml) : previousStatusBlob?.statusUpdateReasonHtml, fcmTopic: ((ruleStatus.fcmTopic != null) || (ruleStatus.code != null)) ? ruleStatus.fcmTopic : previousStatusBlob?.fcmTopic, historyBlob: historyBlob, ) : null; @@ -273,6 +279,10 @@ class HealthStatusBlob { return _processMacros(statusUpdateReason); } + String get displayStatusUpdateReasonHtml { + return _processMacros(statusUpdateReasonHtml); + } + String _processMacros(String value) { if ((value != null) && (nextStepDateUtc != null) && value.contains(_nextStepDateMacro)) { return value.replaceAll(_nextStepDateMacro, displayNextStepDate() ?? ''); @@ -3414,6 +3424,7 @@ class HealthRuleStatus extends _HealthRuleStatus { final dynamic eventExplanationHtml; final dynamic statusUpdateReason; + final dynamic statusUpdateReasonHtml; final dynamic fcmTopic; @@ -3421,40 +3432,42 @@ class HealthRuleStatus extends _HealthRuleStatus { this.nextStep, this.nextStepHtml, this.nextStepInterval, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, - this.statusUpdateReason, + this.statusUpdateReason, this.statusUpdateReasonHtml, this.fcmTopic }); factory HealthRuleStatus.fromJson(Map json) { return (json != null) ? HealthRuleStatus( - code: json['code'], - priority: json['priority'], - nextStep: json['next_step'], - nextStepHtml: json['next_step_html'], - nextStepInterval: _HealthRuleInterval.fromJson(json['next_step_interval']), - warning: json['warning'], - warningHtml: json['warning_html'], - eventExplanation: json['event_explanation'], - eventExplanationHtml: json['event_explanation_html'], - statusUpdateReason: json['status_update_reason'] ?? json['reason'], - fcmTopic: json['fcm_topic'] + code: json['code'], + priority: json['priority'], + nextStep: json['next_step'], + nextStepHtml: json['next_step_html'], + nextStepInterval: _HealthRuleInterval.fromJson(json['next_step_interval']), + warning: json['warning'], + warningHtml: json['warning_html'], + eventExplanation: json['event_explanation'], + eventExplanationHtml: json['event_explanation_html'], + statusUpdateReason: json['status_update_reason'] ?? json['reason'], + statusUpdateReasonHtml: json['status_update_reason_html'] ?? json['reason_html'], + fcmTopic: json['fcm_topic'] ) : null; } factory HealthRuleStatus.fromStatus(HealthRuleStatus status, { DateTime nextStepDateUtc, }) { return (status != null) ? HealthRuleStatus( - code: status.code, - priority: status.priority, - nextStep: status.nextStep, - nextStepHtml: status.nextStepHtml, - nextStepInterval: status.nextStepInterval, - nextStepDateUtc: nextStepDateUtc ?? status.nextStepDateUtc, - warning: status.warning, - warningHtml: status.warningHtml, - eventExplanation: status.eventExplanation, - eventExplanationHtml: status.eventExplanationHtml, - statusUpdateReason: status.statusUpdateReason, - fcmTopic: status.fcmTopic, + code: status.code, + priority: status.priority, + nextStep: status.nextStep, + nextStepHtml: status.nextStepHtml, + nextStepInterval: status.nextStepInterval, + nextStepDateUtc: nextStepDateUtc ?? status.nextStepDateUtc, + warning: status.warning, + warningHtml: status.warningHtml, + eventExplanation: status.eventExplanation, + eventExplanationHtml: status.eventExplanationHtml, + statusUpdateReason: status.statusUpdateReason, + statusUpdateReasonHtml: status.statusUpdateReasonHtml, + fcmTopic: status.fcmTopic, ) : null; } @@ -3475,6 +3488,7 @@ class HealthRuleStatus extends _HealthRuleStatus { (o.eventExplanationHtml == eventExplanationHtml) && (o.statusUpdateReason == statusUpdateReason) && + (o.statusUpdateReasonHtml == statusUpdateReasonHtml) && (o.fcmTopic == fcmTopic); @@ -3494,6 +3508,7 @@ class HealthRuleStatus extends _HealthRuleStatus { (eventExplanationHtml?.hashCode ?? 0) ^ (statusUpdateReason?.hashCode ?? 0) ^ + (statusUpdateReasonHtml?.hashCode ?? 0) ^ (fcmTopic?.hashCode ?? 0); diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 5fb1aa2b..33b59aae 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -38,7 +38,8 @@ import 'package:illinois/utils/Utils.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import "package:pointycastle/export.dart"; -//TMP: import 'package:flutter/services.dart' show rootBundle; +//TMP: +import 'package:flutter/services.dart' show rootBundle; class Health with Service implements NotificationsListener { @@ -1537,7 +1538,8 @@ class Health with Service implements NotificationsListener { } Future _loadRulesJsonStringFromNet({String countyId}) async { -//TMP: return await rootBundle.loadString('assets/health.rules.json'); +//TMP: +return await rootBundle.loadString('assets/health.rules.json'); countyId = countyId ?? _county?.id; String url = ((countyId != null) && (Config().healthUrl != null)) ? "${Config().healthUrl}/covid19/crules/county/$countyId" : null; String appVersion = AppVersion.majorVersion(Config().appVersion, 2); diff --git a/lib/ui/health/HealthStatusUpdatePanel.dart b/lib/ui/health/HealthStatusUpdatePanel.dart index f978a5f2..d1534967 100644 --- a/lib/ui/health/HealthStatusUpdatePanel.dart +++ b/lib/ui/health/HealthStatusUpdatePanel.dart @@ -16,8 +16,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_html/style.dart'; import 'package:illinois/model/Health.dart'; +import 'package:illinois/service/Connectivity.dart'; import 'package:illinois/service/Health.dart'; +import 'package:illinois/ui/WebPanel.dart'; import 'package:illinois/utils/AppDateTime.dart'; import 'package:illinois/service/Localization.dart'; import 'package:illinois/service/Styles.dart'; @@ -25,6 +29,7 @@ import 'package:illinois/ui/health/HealthNextStepsPanel.dart'; import 'package:illinois/ui/widgets/RoundedButton.dart'; import 'package:illinois/ui/widgets/StatusInfoDialog.dart'; import 'package:illinois/utils/Utils.dart'; +import 'package:url_launcher/url_launcher.dart'; class HealthStatusUpdatePanel extends StatefulWidget { final HealthStatus status; @@ -163,6 +168,7 @@ class _HealthStatusUpdatePanelState extends State { Widget _buildReasonContent(){ String date = AppDateTime.formatDateTime(widget.status?.dateUtc?.toLocal(), format: "MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode); String reasonText = widget.status?.blob?.displayStatusUpdateReason; + String reasonHtml = widget.status?.blob?.displayStatusUpdateReasonHtml; HealthHistoryBlob reasonHistory = widget.status?.blob?.historyBlob; String reasonHistoryName; @@ -231,7 +237,7 @@ class _HealthStatusUpdatePanelState extends State { } } - if ((reasonText != null) || (reasonHistoryName != null) || (reasonHistoryDetail != null)) { + if ((reasonText != null) || (reasonHtml != null) || (reasonHistoryName != null) || (reasonHistoryDetail != null)) { List content = [ Container(height: 30,), Text(Localization().getStringEx("panel.health.status_update.label.reason.title", "STATUS CHANGED BECAUSE:"), textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold),), @@ -258,12 +264,27 @@ class _HealthStatusUpdatePanelState extends State { ]); } - if (reasonText != null) { - content.addAll([ - Container(height: 60,), - Text(reasonText, textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 14, fontFamily: Styles().fontFamilies.bold),), - Container(height: 30,), - ]); + if ((reasonText != null) || (reasonHtml != null)) { + content.add(Container(height: 60,)); + + if (reasonText != null) { + content.add(Text(reasonText, textAlign: TextAlign.center, style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 14, color: Styles().colors.white,),)); + } + + if ((reasonText != null) && (reasonHtml != null)) { + content.add(Container(height: 12,)); + } + + if (reasonHtml != null) { + content.add(Html(data: reasonHtml, onLinkTap: (url) => _onTapLink(url), + style: { + "body": Style(fontFamily: Styles().fontFamilies.medium, fontSize: FontSize(14), color: Styles().colors.white, padding: EdgeInsets.zero, margin: EdgeInsets.zero), + }, + ), +); + } + + content.add(Container(height: 30,)); } return Container( @@ -304,4 +325,18 @@ class _HealthStatusUpdatePanelState extends State { ]) ); }*/ + + void _onTapLink(String url) { + if (Connectivity().isNotOffline) { + if (AppString.isStringNotEmpty(url)) { + if (AppUrl.launchInternal(url)) { + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: url))); + } else { + launch(url); + } + } + } else { + AppAlert.showOfflineMessage(context); + } + } } \ No newline at end of file From 236609c2be105b215bb7a9504e97fe0f4df3e0f4 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:00:14 +0300 Subject: [PATCH 38/73] Disabled debug shortcuts checked in in previous commit. --- lib/service/Health.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 33b59aae..5fb1aa2b 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -38,8 +38,7 @@ import 'package:illinois/utils/Utils.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import "package:pointycastle/export.dart"; -//TMP: -import 'package:flutter/services.dart' show rootBundle; +//TMP: import 'package:flutter/services.dart' show rootBundle; class Health with Service implements NotificationsListener { @@ -1538,8 +1537,7 @@ class Health with Service implements NotificationsListener { } Future _loadRulesJsonStringFromNet({String countyId}) async { -//TMP: -return await rootBundle.loadString('assets/health.rules.json'); +//TMP: return await rootBundle.loadString('assets/health.rules.json'); countyId = countyId ?? _county?.id; String url = ((countyId != null) && (Config().healthUrl != null)) ? "${Config().healthUrl}/covid19/crules/county/$countyId" : null; String appVersion = AppVersion.majorVersion(Config().appVersion, 2); From a3f9f8821d1151801deb89462b2273f47e69cfcc Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:01:21 +0300 Subject: [PATCH 39/73] Added statusUpdateNotice field, use simply 'notice' and 'reason' for JSON representation instead of 'status_update_notice' and 'status_update_reason' (#702). --- lib/model/Health.dart | 48 +++++++++-- lib/ui/health/HealthStatusUpdatePanel.dart | 95 ++++++++++++---------- 2 files changed, 95 insertions(+), 48 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 423453fd..d894d95a 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -132,6 +132,9 @@ class HealthStatusBlob { final String eventExplanation; final String eventExplanationHtml; + final String statusUpdateNotice; + final String statusUpdateNoticeHtml; + final String statusUpdateReason; final String statusUpdateReasonHtml; @@ -146,6 +149,7 @@ class HealthStatusBlob { this.nextStep, this.nextStepHtml, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, + this.statusUpdateNotice, this.statusUpdateNoticeHtml, this.statusUpdateReason, this.statusUpdateReasonHtml, this.fcmTopic, this.historyBlob}); @@ -160,8 +164,10 @@ class HealthStatusBlob { warningHtml: json['warning_html'], eventExplanation: json['event_explanation'], eventExplanationHtml: json['event_explanation_html'], - statusUpdateReason: json['status_update_reason'] ?? json['reason'], - statusUpdateReasonHtml: json['status_update_reason_html'] ?? json['reason_html'], + statusUpdateNotice: json['notice'], + statusUpdateNoticeHtml: json['notice_html'], + statusUpdateReason: json['reason'], + statusUpdateReasonHtml: json['reason_html'], fcmTopic: json['fcm_topic'], historyBlob: HealthHistoryBlob.fromJson(json['history_blob']), ) : null; @@ -178,8 +184,10 @@ class HealthStatusBlob { 'warning_html': warningHtml, 'event_explanation': eventExplanation, 'event_explanation_html': eventExplanationHtml, - 'status_update_reason': statusUpdateReason, - 'status_update_reason_html': statusUpdateReasonHtml, + 'notice': statusUpdateNotice, + 'notice_html': statusUpdateNoticeHtml, + 'reason': statusUpdateReason, + 'reason_html': statusUpdateReasonHtml, 'fcm_topic': fcmTopic, 'history_blob': historyBlob?.toJson(), }; @@ -196,6 +204,8 @@ class HealthStatusBlob { (o.warningHtml == warningHtml) && (o.eventExplanation == eventExplanation) && (o.eventExplanationHtml == eventExplanationHtml) && + (o.statusUpdateNotice == statusUpdateNotice) && + (o.statusUpdateNoticeHtml == statusUpdateNoticeHtml) && (o.statusUpdateReason == statusUpdateReason) && (o.statusUpdateReasonHtml == statusUpdateReasonHtml) && DeepCollectionEquality().equals(o.fcmTopic, fcmTopic) && @@ -212,6 +222,8 @@ class HealthStatusBlob { (warningHtml?.hashCode ?? 0) ^ (eventExplanation?.hashCode ?? 0) ^ (eventExplanationHtml?.hashCode ?? 0) ^ + (statusUpdateNotice?.hashCode ?? 0) ^ + (statusUpdateNoticeHtml?.hashCode ?? 0) ^ (statusUpdateReason?.hashCode ?? 0) ^ (statusUpdateReasonHtml?.hashCode ?? 0) ^ (DeepCollectionEquality().hash(fcmTopic) ?? 0) ^ @@ -228,6 +240,8 @@ class HealthStatusBlob { eventExplanationHtml: ((ruleStatus.eventExplanation != null) || (ruleStatus.eventExplanationHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.eventExplanationHtml) : previousStatusBlob?.eventExplanationHtml, warning: ((ruleStatus.warning != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warning) : previousStatusBlob?.warning, warningHtml: ((ruleStatus.warningHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.warningHtml) : previousStatusBlob?.warningHtml, + statusUpdateNotice: ((ruleStatus.statusUpdateNotice != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateNotice) : previousStatusBlob?.statusUpdateNotice, + statusUpdateNoticeHtml: ((ruleStatus.statusUpdateNoticeHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateNoticeHtml) : previousStatusBlob?.statusUpdateNoticeHtml, statusUpdateReason: ((ruleStatus.statusUpdateReason != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReason) : previousStatusBlob?.statusUpdateReason, statusUpdateReasonHtml: ((ruleStatus.statusUpdateReasonHtml != null) || (ruleStatus.code != null)) ? rules.localeString(ruleStatus.statusUpdateReasonHtml) : previousStatusBlob?.statusUpdateReasonHtml, fcmTopic: ((ruleStatus.fcmTopic != null) || (ruleStatus.code != null)) ? ruleStatus.fcmTopic : previousStatusBlob?.fcmTopic, @@ -275,6 +289,14 @@ class HealthStatusBlob { return _processMacros(eventExplanationHtml); } + String get displayStatusUpdateNotice { + return _processMacros(statusUpdateNotice); + } + + String get displayStatusUpdateNoticeHtml { + return _processMacros(statusUpdateNoticeHtml); + } + String get displayStatusUpdateReason { return _processMacros(statusUpdateReason); } @@ -3423,6 +3445,9 @@ class HealthRuleStatus extends _HealthRuleStatus { final dynamic eventExplanation; final dynamic eventExplanationHtml; + final dynamic statusUpdateNotice; + final dynamic statusUpdateNoticeHtml; + final dynamic statusUpdateReason; final dynamic statusUpdateReasonHtml; @@ -3432,6 +3457,7 @@ class HealthRuleStatus extends _HealthRuleStatus { this.nextStep, this.nextStepHtml, this.nextStepInterval, this.nextStepDateUtc, this.warning, this.warningHtml, this.eventExplanation, this.eventExplanationHtml, + this.statusUpdateNotice, this.statusUpdateNoticeHtml, this.statusUpdateReason, this.statusUpdateReasonHtml, this.fcmTopic }); @@ -3446,8 +3472,10 @@ class HealthRuleStatus extends _HealthRuleStatus { warningHtml: json['warning_html'], eventExplanation: json['event_explanation'], eventExplanationHtml: json['event_explanation_html'], - statusUpdateReason: json['status_update_reason'] ?? json['reason'], - statusUpdateReasonHtml: json['status_update_reason_html'] ?? json['reason_html'], + statusUpdateNotice: json['notice'], + statusUpdateNoticeHtml: json['notice_html'], + statusUpdateReason: json['reason'], + statusUpdateReasonHtml: json['reason_html'], fcmTopic: json['fcm_topic'] ) : null; } @@ -3465,6 +3493,8 @@ class HealthRuleStatus extends _HealthRuleStatus { warningHtml: status.warningHtml, eventExplanation: status.eventExplanation, eventExplanationHtml: status.eventExplanationHtml, + statusUpdateNotice: status.statusUpdateNotice, + statusUpdateNoticeHtml: status.statusUpdateNoticeHtml, statusUpdateReason: status.statusUpdateReason, statusUpdateReasonHtml: status.statusUpdateReasonHtml, fcmTopic: status.fcmTopic, @@ -3487,6 +3517,9 @@ class HealthRuleStatus extends _HealthRuleStatus { (o.eventExplanation == eventExplanation) && (o.eventExplanationHtml == eventExplanationHtml) && + (o.statusUpdateNotice == statusUpdateNotice) && + (o.statusUpdateNoticeHtml == statusUpdateNoticeHtml) && + (o.statusUpdateReason == statusUpdateReason) && (o.statusUpdateReasonHtml == statusUpdateReasonHtml) && @@ -3507,6 +3540,9 @@ class HealthRuleStatus extends _HealthRuleStatus { (eventExplanation?.hashCode ?? 0) ^ (eventExplanationHtml?.hashCode ?? 0) ^ + (statusUpdateNotice?.hashCode ?? 0) ^ + (statusUpdateNoticeHtml?.hashCode ?? 0) ^ + (statusUpdateReason?.hashCode ?? 0) ^ (statusUpdateReasonHtml?.hashCode ?? 0) ^ diff --git a/lib/ui/health/HealthStatusUpdatePanel.dart b/lib/ui/health/HealthStatusUpdatePanel.dart index d1534967..5b69a825 100644 --- a/lib/ui/health/HealthStatusUpdatePanel.dart +++ b/lib/ui/health/HealthStatusUpdatePanel.dart @@ -159,37 +159,42 @@ class _HealthStatusUpdatePanelState extends State { padding: EdgeInsets.symmetric(horizontal: 8), child: Container(height: 1, color: Styles().colors.surfaceAccent ,), ), - _buildReasonContent(), + _buildDetails(), ], ), ); } - Widget _buildReasonContent(){ + Widget _buildDetails(){ String date = AppDateTime.formatDateTime(widget.status?.dateUtc?.toLocal(), format: "MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode); + + HealthHistoryBlob history = widget.status?.blob?.historyBlob; + String reasonText = widget.status?.blob?.displayStatusUpdateReason; String reasonHtml = widget.status?.blob?.displayStatusUpdateReasonHtml; - HealthHistoryBlob reasonHistory = widget.status?.blob?.historyBlob; - String reasonHistoryName; - Widget reasonHistoryDetail; - if (reasonHistory != null) { - if (reasonHistory.isTest) { - reasonHistoryName = reasonHistory.testType; + String noticeText = widget.status?.blob?.displayStatusUpdateNotice; + String noticeHtml = widget.status?.blob?.displayStatusUpdateNoticeHtml; + Widget noticeDetailWidget; + + if ((noticeText == null) && (noticeHtml == null) && (history != null)) { + // Build default notice content + if (history.isTest) { + noticeText = history.testType; - reasonHistoryDetail = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + noticeDetailWidget = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("images/icon-selected.png", excludeFromSemantics: true,), Container(width: 7,), Text(Localization().getStringEx("panel.health.status_update.label.reason.result", "Result:"), style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold)), Container(width: 5,), - Text(reasonHistory.testResult ?? '', style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), + Text(history.testResult ?? '', style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), ],); } - else if (reasonHistory.isSymptoms) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.symptoms.title", "You reported new symptoms"); + else if (history.isSymptoms) { + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.symptoms.title", "You reported new symptoms"); List symptomLayouts = []; - List symptoms = reasonHistory.symptoms; + List symptoms = history.symptoms; if (symptoms?.isNotEmpty ?? false) { symptoms.forEach((HealthSymptom symptom){ String symptomName = Health().rules?.localeString(symptom?.name) ?? symptom?.name; @@ -199,45 +204,45 @@ class _HealthStatusUpdatePanelState extends State { }); } - reasonHistoryDetail = Column(mainAxisAlignment: MainAxisAlignment.center, children: symptomLayouts,); + noticeDetailWidget = Column(mainAxisAlignment: MainAxisAlignment.center, children: symptomLayouts,); } - else if (reasonHistory.isContactTrace) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.exposed.title", "You were exposed to someone who was likely infected"); + else if (history.isContactTrace) { + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.exposed.title", "You were exposed to someone who was likely infected"); - reasonHistoryDetail = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + noticeDetailWidget = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("images/icon-selected.png", excludeFromSemantics: true,), Container(width: 7,), Text(Localization().getStringEx("panel.health.status_update.label.reason.exposure.detail", "Duration of exposure: "), style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold)), Container(width: 5,), - Text(reasonHistory.traceDurationDisplayString ?? "", style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), + Text(history.traceDurationDisplayString ?? "", style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), ],); } - else if (reasonHistory.isVaccine) { - if (reasonHistory.isVaccineEffective) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.effective.title", "Your COVID-19 vaccination has been verified."); + else if (history.isVaccine) { + if (history.isVaccineEffective) { + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.effective.title", "Your COVID-19 vaccination has been verified."); } - else if (reasonHistory.isVaccineTaken) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.taken.title", "Your vaccine is taken."); + else if (history.isVaccineTaken) { + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.taken.title", "Your vaccine is taken."); } else { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.title", "Your vaccine is applied."); + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.title", "Your vaccine is applied."); } } - else if (reasonHistory.isAction) { - reasonHistoryName = Localization().getStringEx("panel.health.status_update.label.reason.action.title", "Health authorities require you to take an action."); + else if (history.isAction) { + noticeText = Localization().getStringEx("panel.health.status_update.label.reason.action.title", "Health authorities require you to take an action."); - reasonHistoryDetail = Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ + noticeDetailWidget = Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset("images/icon-selected.png", excludeFromSemantics: true,), Container(width: 7,), - Text(reasonHistory.localeActionTitle ?? Localization().getStringEx("panel.health.status_update.label.reason.action.detail", "Action Required: "), style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold)), + Text(history.localeActionTitle ?? Localization().getStringEx("panel.health.status_update.label.reason.action.detail", "Action Required: "), style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold)), ],), - Text(reasonHistory.localeActionText ?? "", style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), + Text(history.localeActionText ?? "", style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.regular)), ],); } } - if ((reasonText != null) || (reasonHtml != null) || (reasonHistoryName != null) || (reasonHistoryDetail != null)) { + if ((reasonText != null) || (reasonHtml != null) || (noticeText != null) || (noticeHtml != null) || (noticeDetailWidget != null)) { List content = [ Container(height: 30,), Text(Localization().getStringEx("panel.health.status_update.label.reason.title", "STATUS CHANGED BECAUSE:"), textAlign: TextAlign.center, style: TextStyle(color: Colors.white, fontSize: 12, fontFamily: Styles().fontFamilies.bold),), @@ -251,37 +256,43 @@ class _HealthStatusUpdatePanelState extends State { ]); } - if (reasonHistoryName != null) { - content.addAll([ - Text(reasonHistoryName,textAlign: TextAlign.center,style: TextStyle(color: Colors.white, fontSize: 18, fontFamily: Styles().fontFamilies.extraBold),), - Container(height: 9,), - ]); + if ((noticeText != null) || (noticeHtml != null)) { + if (noticeText != null) { + content.add(Text(noticeText, textAlign: TextAlign.center, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 18, color: Colors.white, ),), ); + } + if ((noticeText != null) && (noticeHtml != null)) { + content.add(Container(height: 9,)); + } + if (noticeHtml != null) { + content.add(Html(data: noticeHtml, onLinkTap: (url) => _onTapLink(url), + style: { + "body": Style(fontFamily: Styles().fontFamilies.medium, fontSize: FontSize(18), color: Styles().colors.white, padding: EdgeInsets.zero, margin: EdgeInsets.zero), + }, + ),); + } + content.add(Container(height: 9,)); } - if (reasonHistoryDetail != null) { + if (noticeDetailWidget != null) { content.addAll([ - reasonHistoryDetail, + noticeDetailWidget, ]); } if ((reasonText != null) || (reasonHtml != null)) { content.add(Container(height: 60,)); - if (reasonText != null) { content.add(Text(reasonText, textAlign: TextAlign.center, style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 14, color: Styles().colors.white,),)); } - if ((reasonText != null) && (reasonHtml != null)) { content.add(Container(height: 12,)); } - if (reasonHtml != null) { content.add(Html(data: reasonHtml, onLinkTap: (url) => _onTapLink(url), style: { "body": Style(fontFamily: Styles().fontFamilies.medium, fontSize: FontSize(14), color: Styles().colors.white, padding: EdgeInsets.zero, margin: EdgeInsets.zero), }, - ), -); + ),); } content.add(Container(height: 30,)); From 42dada1e763592c65ed83c4d56da4f4c61136553 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:09:53 +0300 Subject: [PATCH 40/73] Acknowledge 'notice' field for "vaccinated.notice" case (#702). --- assets/health.rules.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/health.rules.json b/assets/health.rules.json index 87d43855..d9ed979b 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -341,6 +341,7 @@ "code": "green", "priority": 4, "next_step_html": "vaccinated.step.html", + "notice": "vaccinated.notice", "reason": "vaccinated.reason", "fcm_topic": "vaccinated" }, @@ -349,6 +350,7 @@ "code": "green", "priority": -4, "next_step_html": "vaccinated.step.html", + "notice": "vaccinated.notice", "reason": "vaccinated.reason", "fcm_topic": "vaccinated" }, @@ -981,6 +983,7 @@ "exposure.step.html": "

You have likely been exposed to a person who is infected with COVID-19.

  • You must quarantine yourself immediately.
  • Stay home. Do not go to work, school, or public areas.
  • Separate yourself from others in your home.
  • Contact covidwellness@illinois.edu for guidance.
  • Get tested after {next_step_date} to see if you have developed the disease.
  • More Info: Quarantine and Isolation
", "exposure.reason": "Your status changed to Orange because you received an exposure notification.", "vaccinated.step.html": "

We have a verified record of your completed COVID-19 vaccination on file.

Your vaccination status replaces testing for compliance and building access until further notice.

Please get an on-campus COVID-19 test if you experience symptoms.

Continue to monitor university communications for any changes to your testing policy.

", + "vaccinated.notice": "Your COVID-19 vaccination has been verified", "vaccinated.reason": "We have a verified record of your completed COVID-19 vaccination on file; your status has changed to Green.", "vaccinated.suspended.reason": "Your status change to Orange because you have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time.", "quarantine-on.step": "Stay at home and avoid contacts", @@ -1055,6 +1058,7 @@ "exposure.step.html": "

Es probable que haya estado expuesto a una persona infectada con COVID-19.

  • Debe ponerse en cuarentena inmediatamente.
  • Quedarse en casa. No vaya al trabajo, la escuela o áreas públicas.
  • Sepárate de las demás en tu casa.
  • Póngase en contacto con covidwellness@illinois.edu para obtener orientación.
  • Hágase la prueba después del {next_step_date} para ver si ha desarrollado la enfermedad.
  • Más información: Cuarentena y aislamiento
", "exposure.reason": "Su estado cambió a Naranja porque recibió una notificación de exposición.", "vaccinated.step.html": "

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Su estado de vacunación reemplaza las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.

Hágase una prueba de COVID-19 en el campus si experimenta síntomas.

Continúe monitoreando las comunicaciones de la universidad para detectar cualquier cambio en su política de exámenes.

", + "vaccinated.notice": "Se ha verificado su vacuna COVID-19.", "vaccinated.reason": "Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo; su estado ha cambiado a verde.", "vaccinated.suspended.reason": "Su estado cambia a Naranja porque se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo.", "quarantine-on.step": "Quédese en casa y evite los contactos", @@ -1129,6 +1133,7 @@ "exposure.step.html": "

您可能已經接觸了感染了COVID-19的人。

  • 您必須立即隔離自己。
  • 您已被強制隔離。
  • 與家中的其他人分開。
  • 請與 covidwellness@illinois.edu 聯繫以獲取指導。
  • 在{next_step_date}之後進行測試,看看您是否已患上這種疾病。
  • 更多信息: 隔離與隔離
", "exposure.reason": "您的狀態更改為橙色,因為您收到了曝光通知。", "vaccinated.step.html": "

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您的疫苗接種狀態將取代合規性測試和建立訪問權限,直至另行通知。

如果您出現症狀,請進行校園 COVID-19 測試。

繼續監控大學通訊,了解您的考試政策是否有任何變化。

", + "vaccinated.notice": "COVID-19ワクチン接種が確認されました。", "vaccinated.reason": "我們有您完成的 COVID-19 疫苗接種的經過驗證的記錄存檔; 您的狀態已更改為綠色。", "vaccinated.suspended.reason": "您的狀態更改為橙色,因為您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。", "quarantine-on.step": "呆在家裡,避免接觸", @@ -1203,6 +1208,7 @@ "exposure.step.html": "

COVID-19に感染している人と接触した可能性があります。

  • 直ちに隔離してください。
  • 家にいてください。仕事や学校や公共の場へ行かないでください。
  • 家にいる人から離れてください。
  • ガイダンスを受けるにはcovidwellness@illinois.eduに連絡してください。
  • {next_step_date}の後で検査を受けて、自分が発症しているかどうかを確認してください。
  • より詳しい情報: 隔離と孤立
", "exposure.reason": "接触通知を受信したため、ステータスがオレンジに変更されました。", "vaccinated.step.html": "

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたのワクチン接種状況は、追って通知があるまで、コンプライアンスと建物へのアクセスのテストに取って代わります。

症状が出た場合は、キャンパス内のCOVID-19検査を受けてください。

テストポリシーに変更がないか、大学のコミュニケーションを引き続き監視します。

", + "vaccinated.notice": "您的 COVID-19 疫苗接種已通過驗證。", "vaccinated.reason": "完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。 ステータスが緑に変わりました。", "vaccinated.suspended.reason": "短期間に陽性のCOVID-19症例が大幅に増加している地域にいることが確認されたため、ステータスがオレンジに変わります。", "quarantine-on.step": "家にいて、他の人との接触を避けてください。", From 10b270f91190c682177a743c284bcc7593a4656e Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:26:12 +0300 Subject: [PATCH 41/73] Defined "vaccinated.suspended.notice" in health rules (#702). --- assets/health.rules.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/health.rules.json b/assets/health.rules.json index d9ed979b..f26e2176 100644 --- a/assets/health.rules.json +++ b/assets/health.rules.json @@ -359,6 +359,7 @@ "code": null, "priority": null, "next_step_html": "test.now.vaccinated.step.html", + "notice": "vaccinated.suspended.notice", "reason": "vaccinated.suspended.reason" }, @@ -985,6 +986,7 @@ "vaccinated.step.html": "

We have a verified record of your completed COVID-19 vaccination on file.

Your vaccination status replaces testing for compliance and building access until further notice.

Please get an on-campus COVID-19 test if you experience symptoms.

Continue to monitor university communications for any changes to your testing policy.

", "vaccinated.notice": "Your COVID-19 vaccination has been verified", "vaccinated.reason": "We have a verified record of your completed COVID-19 vaccination on file; your status has changed to Green.", + "vaccinated.suspended.notice": "Your vaccination status is replaced by testing for compliance and building access.", "vaccinated.suspended.reason": "Your status change to Orange because you have been identified as being in an area with a significant increase in positive COVID-19 cases over a short period of time.", "quarantine-on.step": "Stay at home and avoid contacts", "quarantine-on.reason": "Your status changed to Orange because the Public Health department placed you in Quarantine.", @@ -1060,6 +1062,7 @@ "vaccinated.step.html": "

Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo.

Su estado de vacunación reemplaza las pruebas de cumplimiento y acceso al edificio hasta nuevo aviso.

Hágase una prueba de COVID-19 en el campus si experimenta síntomas.

Continúe monitoreando las comunicaciones de la universidad para detectar cualquier cambio en su política de exámenes.

", "vaccinated.notice": "Se ha verificado su vacuna COVID-19.", "vaccinated.reason": "Tenemos un registro verificado de su vacunación COVID-19 completa en el archivo; su estado ha cambiado a verde.", + "vaccinated.suspended.notice": "Su estado de vacunación se reemplaza por pruebas de cumplimiento y acceso al edificio.", "vaccinated.suspended.reason": "Su estado cambia a Naranja porque se ha identificado que se encuentra en un área con un aumento significativo de casos positivos de COVID-19 durante un corto período de tiempo.", "quarantine-on.step": "Quédese en casa y evite los contactos", "quarantine-on.reason": "Su estado cambió a Orange porque el departamento de Salud Pública lo puso en cuarentena.", @@ -1135,6 +1138,7 @@ "vaccinated.step.html": "

我們記錄了您完成的COVID-19疫苗接種的經過驗證的記錄。

您的疫苗接種狀態將取代合規性測試和建立訪問權限,直至另行通知。

如果您出現症狀,請進行校園 COVID-19 測試。

繼續監控大學通訊,了解您的考試政策是否有任何變化。

", "vaccinated.notice": "COVID-19ワクチン接種が確認されました。", "vaccinated.reason": "我們有您完成的 COVID-19 疫苗接種的經過驗證的記錄存檔; 您的狀態已更改為綠色。", + "vaccinated.suspended.notice": "您的疫苗接種狀態將被合規性測試和建築物訪問所取代。", "vaccinated.suspended.reason": "您的狀態更改為橙色,因為您已被確定為在短時間內 COVID-19 陽性病例顯著增加的地區。", "quarantine-on.step": "呆在家裡,避免接觸", "quarantine-on.reason": "您的狀態更改為“橙色”,因為公共衛生部門已將您隔離。", @@ -1210,6 +1214,7 @@ "vaccinated.step.html": "

完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。

あなたのワクチン接種状況は、追って通知があるまで、コンプライアンスと建物へのアクセスのテストに取って代わります。

症状が出た場合は、キャンパス内のCOVID-19検査を受けてください。

テストポリシーに変更がないか、大学のコミュニケーションを引き続き監視します。

", "vaccinated.notice": "您的 COVID-19 疫苗接種已通過驗證。", "vaccinated.reason": "完了したCOVID-19ワクチン接種の確認済みの記録がファイルにあります。 ステータスが緑に変わりました。", + "vaccinated.suspended.notice": "予防接種のステータスは、コンプライアンスと建物へのアクセスのテストに置き換えられます。", "vaccinated.suspended.reason": "短期間に陽性のCOVID-19症例が大幅に増加している地域にいることが確認されたため、ステータスがオレンジに変わります。", "quarantine-on.step": "家にいて、他の人との接触を避けてください。", "quarantine-on.reason": "Public Health departmentにより隔離状態に置かれているため、ステータスがオレンジに変更されました。", From 97f122753a9a012684306af012e554b3de8323f7 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:28:41 +0300 Subject: [PATCH 42/73] Updated CHANGELOG.md (#702). --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb3ba90c..a7575f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Updated strings for vaccination effective status change [#698](https://github.com/rokwire/safer-illinois-app/issues/698). - Updated strings for next step for vaccinated users in UIN override list who has fulfilled their tests [#700](https://github.com/rokwire/safer-illinois-app/issues/700). +- Defined notice, noticeHtml, reason and reasonHtml in health status definitions. Acknowledged for vaccination and vaccination suspension events. [#702](https://github.com/rokwire/safer-illinois-app/issues/702). ## [2.10.36] - 2021-09-01 ### Changed From 13afe3a876e495bddfbb6d68a2fbbaec6af1d4be Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Tue, 7 Sep 2021 16:43:46 +0300 Subject: [PATCH 43/73] version: 2.10.37+1037 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7575f59..da81843a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.37] - 2021-09-07 ### Changed - Updated strings for vaccination effective status change [#698](https://github.com/rokwire/safer-illinois-app/issues/698). - Updated strings for next step for vaccinated users in UIN override list who has fulfilled their tests [#700](https://github.com/rokwire/safer-illinois-app/issues/700). diff --git a/pubspec.yaml b/pubspec.yaml index 8f18958a..acd8c6bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.36+1036 +version: 2.10.37+1037 environment: sdk: ">=2.2.0 <3.0.0" From d7fdc63833bd516fcff556ec2f41de30d48c6b95 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 8 Sep 2021 14:17:25 +0300 Subject: [PATCH 44/73] Fixed Health model comparison operator: use DeepCollectionEquality instead of ListEquality and MapEquality; fixed HealthTestRuleResult equality operator. --- lib/model/Health.dart | 84 +++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index d894d95a..4cc73b6a 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -521,7 +521,7 @@ class HealthHistory implements Comparable { (this.blob?.providerId == event?.providerId) && (this.blob?.testType == event?.blob?.testType) && (this.blob?.testResult == event?.blob?.testResult) && - (ListEquality().equals(this.blob.extras, event.blob.extras)); + (DeepCollectionEquality().equals(this.blob.extras, event.blob.extras)); } else if (event.isVaccine) { return this.isVaccine && @@ -536,8 +536,8 @@ class HealthHistory implements Comparable { (this.blob?.actionType == event?.blob?.actionType) && (DeepCollectionEquality().equals(this.blob?.actionText, event?.blob?.actionText)) && (DeepCollectionEquality().equals(this.blob?.actionTitle, event?.blob?.actionTitle)) && - (MapEquality().equals(this.blob?.actionParams, event?.blob?.actionParams)) && - (ListEquality().equals(this.blob.extras, event.blob.extras)); + (DeepCollectionEquality().equals(this.blob?.actionParams, event?.blob?.actionParams)) && + (DeepCollectionEquality().equals(this.blob.extras, event.blob.extras)); } else { return false; @@ -806,7 +806,7 @@ class HealthHistoryBlob { (o.testType == testType) && (o.testResult == testResult) && - ListEquality().equals(o.symptoms, symptoms) && + DeepCollectionEquality().equals(o.symptoms, symptoms) && (o.traceDuration == traceDuration) && (o.traceTEK == traceTEK) && @@ -816,9 +816,9 @@ class HealthHistoryBlob { (o.actionType == actionType) && DeepCollectionEquality().equals(o.actionTitle, actionTitle) && DeepCollectionEquality().equals(o.actionText, actionText) && - MapEquality().equals(o.actionParams, actionParams) && + DeepCollectionEquality().equals(o.actionParams, actionParams) && - ListEquality().equals(o.extras, extras); + DeepCollectionEquality().equals(o.extras, extras); } int get hashCode => @@ -830,7 +830,7 @@ class HealthHistoryBlob { (testType?.hashCode ?? 0) ^ (testResult?.hashCode ?? 0) ^ - ListEquality().hash(symptoms) ^ + DeepCollectionEquality().hash(symptoms) ^ (traceDuration?.hashCode ?? 0) ^ (traceTEK?.hashCode ?? 0) ^ @@ -840,9 +840,9 @@ class HealthHistoryBlob { (actionType?.hashCode ?? 0) ^ (DeepCollectionEquality().hash(actionTitle) ?? 0) ^ (DeepCollectionEquality().hash(actionText) ?? 0) ^ - (MapEquality().hash(actionParams) ?? 0) ^ + (DeepCollectionEquality().hash(actionParams) ?? 0) ^ - (ListEquality().hash(extras) ?? 0); + (DeepCollectionEquality().hash(extras) ?? 0); bool get isTest { return (testType != null) && (testResult != null); @@ -1334,7 +1334,7 @@ class HealthUser { o.consentVaccineInformation == consentVaccineInformation && o.consentExposureNotification == consentExposureNotification && o.repost == repost && - ListEquality().equals(o.accounts, accounts) && + DeepCollectionEquality().equals(o.accounts, accounts) && o.encryptedKey == encryptedKey && o.encryptedBlob == encryptedBlob; @@ -1345,7 +1345,7 @@ class HealthUser { (consentVaccineInformation?.hashCode ?? 0) ^ (consentExposureNotification?.hashCode ?? 0) ^ (repost?.hashCode ?? 0) ^ - ListEquality().hash(accounts) ^ + DeepCollectionEquality().hash(accounts) ^ (encryptedKey?.hashCode ?? 0) ^ (encryptedBlob?.hashCode ?? 0); @@ -2591,14 +2591,14 @@ class HealthSymptomsGroup { (o.name == name) && (o.visible == visible) && (o.group == group) && - ListEquality().equals(o.symptoms, symptoms); + DeepCollectionEquality().equals(o.symptoms, symptoms); int get hashCode => (id?.hashCode ?? 0) ^ (name?.hashCode ?? 0) ^ (visible?.hashCode ?? 0) ^ (group?.hashCode ?? 0) ^ - ListEquality().hash(symptoms); + DeepCollectionEquality().hash(symptoms); static Map getCounts(List groups, Set selected) { Map counts = Map(); @@ -2709,8 +2709,8 @@ class HealthRulesSet { (o.actions == actions) && (o.defaults == defaults) && (o.codes == codes) && - MapEquality().equals(o.statuses, statuses) && - MapEquality().equals(o.intervals, intervals) && + DeepCollectionEquality().equals(o.statuses, statuses) && + DeepCollectionEquality().equals(o.intervals, intervals) && DeepCollectionEquality().equals(o.strings, strings); } @@ -2722,8 +2722,8 @@ class HealthRulesSet { (actions?.hashCode ?? 0) ^ (defaults?.hashCode ?? 0) ^ (codes?.hashCode ?? 0) ^ - MapEquality().hash(statuses) ^ - MapEquality().hash(intervals) ^ + DeepCollectionEquality().hash(statuses) ^ + DeepCollectionEquality().hash(intervals) ^ DeepCollectionEquality().hash(strings); _HealthRuleInterval _getInterval(String name) { @@ -2804,12 +2804,12 @@ class HealthCodesSet { bool operator ==(o) => (o is HealthCodesSet) && - ListEquality().equals(o._codesList, _codesList) && - ListEquality().equals(o._info, _info); + DeepCollectionEquality().equals(o._codesList, _codesList) && + DeepCollectionEquality().equals(o._info, _info); int get hashCode => - ListEquality().hash(_codesList) ^ - ListEquality().hash(_info); + DeepCollectionEquality().hash(_codesList) ^ + DeepCollectionEquality().hash(_info); List get list { return _codesList; @@ -2946,10 +2946,10 @@ class HealthTestRulesSet { bool operator ==(o) => (o is HealthTestRulesSet) && - ListEquality().equals(o._rules, _rules); + DeepCollectionEquality().equals(o._rules, _rules); int get hashCode => - ListEquality().hash(_rules); + DeepCollectionEquality().hash(_rules); HealthTestRuleResult matchRuleResult({ HealthHistoryBlob blob, HealthRulesSet rules }) { if ((_rules != null) && (blob != null) && (blob.testType != null) && (blob.testResult != null)) { @@ -2990,12 +2990,12 @@ class HealthTestRule { (o is HealthTestRule) && (o.testType == testType) && (o.category == category) && - ListEquality().equals(o.results, results); + DeepCollectionEquality().equals(o.results, results); int get hashCode => (testType?.hashCode ?? 0) ^ (category?.hashCode ?? 0) ^ - ListEquality().hash(results); + DeepCollectionEquality().hash(results); static List listFromJson(List json) { List values; @@ -3035,7 +3035,7 @@ class HealthTestRuleResult { (o.testResult == testResult) && (o.category == category) && (o.disclaimerHtml == disclaimerHtml) && - (status == status); + (o.status == status); int get hashCode => (testResult?.hashCode ?? 0) ^ @@ -3089,12 +3089,12 @@ class HealthSymptomsRulesSet { bool operator ==(o) => (o is HealthSymptomsRulesSet) && - ListEquality().equals(o._rules, _rules) && - ListEquality().equals(o.groups, groups); + DeepCollectionEquality().equals(o._rules, _rules) && + DeepCollectionEquality().equals(o.groups, groups); int get hashCode => - ListEquality().hash(_rules) ^ - ListEquality().hash(groups); + DeepCollectionEquality().hash(_rules) ^ + DeepCollectionEquality().hash(groups); HealthSymptomsRule matchRule({ HealthHistoryBlob blob, HealthRulesSet rules }) { if ((_rules != null) && (groups != null) && (blob?.symptomsIds != null)) { @@ -3127,11 +3127,11 @@ class HealthSymptomsRule { bool operator ==(o) => (o is HealthSymptomsRule) && - MapEquality().equals(o.counts, counts) && + DeepCollectionEquality().equals(o.counts, counts) && (o.status == status); int get hashCode => - MapEquality().hash(counts) ^ + DeepCollectionEquality().hash(counts) ^ (status?.hashCode ?? 0); static List listFromJson(List json) { @@ -3177,10 +3177,10 @@ class HealthContactTraceRulesSet { bool operator ==(o) => (o is HealthContactTraceRulesSet) && - ListEquality().equals(o._rules, _rules); + DeepCollectionEquality().equals(o._rules, _rules); int get hashCode => - ListEquality().hash(_rules); + DeepCollectionEquality().hash(_rules); HealthContactTraceRule matchRule({ HealthHistoryBlob blob, HealthRulesSet rules }) { @@ -3253,10 +3253,10 @@ class HealthVaccineRulesSet { bool operator ==(o) => (o is HealthVaccineRulesSet) && - ListEquality().equals(o._rules, _rules); + DeepCollectionEquality().equals(o._rules, _rules); int get hashCode => - ListEquality().hash(_rules); + DeepCollectionEquality().hash(_rules); HealthVaccineRule matchRule({ HealthHistoryBlob blob, HealthRulesSet rules }) { if (_rules != null) { @@ -3328,10 +3328,10 @@ class HealthActionRulesSet { bool operator ==(o) => (o is HealthActionRulesSet) && - ListEquality().equals(o._rules, _rules); + DeepCollectionEquality().equals(o._rules, _rules); int get hashCode => - ListEquality().hash(_rules); + DeepCollectionEquality().hash(_rules); HealthActionRule matchRule({ HealthHistoryBlob blob, HealthRulesSet rules }) { if (_rules != null) { @@ -3621,13 +3621,13 @@ class HealthRuleConditionalStatus extends _HealthRuleStatus with HealthRuleCondi bool operator ==(o) => (o is HealthRuleConditionalStatus) && (o.condition == condition) && - (MapEquality().equals(o.conditionParams, conditionParams)) && + (DeepCollectionEquality().equals(o.conditionParams, conditionParams)) && (o.successStatus == successStatus) && (o.failStatus == failStatus); int get hashCode => (condition?.hashCode ?? 0) ^ - (MapEquality().hash(conditionParams)) ^ + (DeepCollectionEquality().hash(conditionParams)) ^ (successStatus?.hashCode ?? 0) ^ (failStatus?.hashCode ?? 0); @@ -4004,13 +4004,13 @@ class HealthRuleIntervalCondition extends _HealthRuleInterval with HealthRuleCon bool operator ==(o) => (o is HealthRuleIntervalCondition) && (o.condition == condition) && - (MapEquality().equals(o.conditionParams, conditionParams)) && + (DeepCollectionEquality().equals(o.conditionParams, conditionParams)) && (o.successInterval == successInterval) && (o.failInterval == failInterval); int get hashCode => (condition?.hashCode ?? 0) ^ - (MapEquality().hash(conditionParams)) ^ + (DeepCollectionEquality().hash(conditionParams)) ^ (successInterval?.hashCode ?? 0) ^ (failInterval?.hashCode ?? 0); From 1d773365750c8a73016a0e2753a9e62d591178d9 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 8 Sep 2021 14:23:15 +0300 Subject: [PATCH 45/73] Storage.healthUserTestMonitorInterval does not default to 0. --- lib/service/Storage.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/service/Storage.dart b/lib/service/Storage.dart index 1fe60b7d..eb0a198c 100644 --- a/lib/service/Storage.dart +++ b/lib/service/Storage.dart @@ -487,7 +487,7 @@ class Storage with Service { static const String _healthUserTestMonitorIntervalKey = 'health_user_test_monitor_interval'; int get healthUserTestMonitorInterval { - return getInt(_healthUserTestMonitorIntervalKey); + return getInt(_healthUserTestMonitorIntervalKey, defaultValue: null); } set healthUserTestMonitorInterval(int value) { From 14e92ddba22d93a1efa56a65b3d644bf93e96a8f Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 8 Sep 2021 17:00:01 +0300 Subject: [PATCH 46/73] Implemented HealthRuleSet.toJson(). --- lib/model/Health.dart | 355 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 345 insertions(+), 10 deletions(-) diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 4cc73b6a..409d6627 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -2700,6 +2700,21 @@ class HealthRulesSet { ) : null; } + Map toJson() { + return { + 'tests': tests?.toJson(), + 'symptoms': symptoms?.toJson(), + 'contact_trace': contactTrace?.toJson(), + 'vaccines': vaccines?.toJson(), + 'actions': actions?.toJson(), + 'defaults': defaults?.toJson(), + 'codes': codes?.toJson(), + 'statuses': _HealthRuleStatus.mapToJson(statuses), + 'intervals': _HealthRuleInterval.mapToJson(intervals), + 'strings': strings, + }; + } + bool operator ==(o) { return (o is HealthRulesSet) && (o.tests == tests) && @@ -2773,6 +2788,12 @@ class HealthDefaultsSet { ) : null; } + Map toJson() { + return { + 'status': status?.toJson(), + }; + } + bool operator ==(o) => (o is HealthDefaultsSet) && (o.status == status); @@ -2802,6 +2823,13 @@ class HealthCodesSet { ) : null; } + Map toJson() { + return { + 'list': HealthCodeData.listToJson(_codesList), + 'info': _info + }; + } + bool operator ==(o) => (o is HealthCodesSet) && DeepCollectionEquality().equals(o._codesList, _codesList) && @@ -2863,6 +2891,18 @@ class HealthCodeData { ) : null; } + Map toJson() { + return { + 'code': code, + 'color': _colorString, + 'name': _name, + 'description': _description, + 'long_description': _longDescription, + 'visible': visible, + 'reports_exposures': reportsExposures + }; + } + bool operator ==(o) => (o is HealthCodeData) && (o.code == code) && @@ -2916,6 +2956,17 @@ class HealthCodeData { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthCodeData value in values) { + json.add(value?.toJson()); + } + } + return json; + } + static Map mapFromList(List list) { Map map; if (list != null) { @@ -2944,6 +2995,12 @@ class HealthTestRulesSet { ) : null; } + Map toJson() { + return { + 'rules': HealthTestRule.listToJson(_rules), + }; + } + bool operator ==(o) => (o is HealthTestRulesSet) && DeepCollectionEquality().equals(o._rules, _rules); @@ -2986,6 +3043,14 @@ class HealthTestRule { ) : null; } + Map toJson() { + return { + 'test_type': testType, + 'category': category, + 'results': HealthTestRuleResult.listToJson(results), + }; + } + bool operator ==(o) => (o is HealthTestRule) && (o.testType == testType) && @@ -3008,6 +3073,17 @@ class HealthTestRule { } return values; } + + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthTestRule value in values) { + json.add(value?.toJson()); + } + } + return json; + } } /////////////////////////////// @@ -3030,6 +3106,15 @@ class HealthTestRuleResult { ) : null; } + Map toJson() { + return { + 'result': testResult, + 'category': category, + 'disclaimer_html': disclaimerHtml, + 'status': status?.toJson(), + }; + } + bool operator ==(o) => (o is HealthTestRuleResult) && (o.testResult == testResult) && @@ -3055,6 +3140,17 @@ class HealthTestRuleResult { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthTestRuleResult value in values) { + json.add(value?.toJson()); + } + } + return json; + } + static HealthTestRuleResult matchRuleResult(List results, { HealthHistoryBlob blob }) { if (results != null) { for (HealthTestRuleResult result in results) { @@ -3087,6 +3183,13 @@ class HealthSymptomsRulesSet { ) : null; } + Map toJson() { + return { + 'rules': HealthSymptomsRule.listToJson(_rules), + 'groups': HealthSymptomsGroup.listToJson(groups), + }; + } + bool operator ==(o) => (o is HealthSymptomsRulesSet) && DeepCollectionEquality().equals(o._rules, _rules) && @@ -3125,6 +3228,13 @@ class HealthSymptomsRule { ) : null; } + Map toJson() { + return { + 'counts': _HealthRuleInterval.mapToJson(counts), + 'status': status?.toJson() + }; + } + bool operator ==(o) => (o is HealthSymptomsRule) && DeepCollectionEquality().equals(o.counts, counts) && @@ -3146,6 +3256,17 @@ class HealthSymptomsRule { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthSymptomsRule value in values) { + json.add(value?.toJson()); + } + } + return json; + } + bool _matchCounts(Map testCounts, { HealthRulesSet rules }) { if (this.counts != null) { for (String groupName in this.counts.keys) { @@ -3175,6 +3296,12 @@ class HealthContactTraceRulesSet { ) : null; } + Map toJson() { + return { + 'rules': HealthContactTraceRule.listToJson(_rules), + }; + } + bool operator ==(o) => (o is HealthContactTraceRulesSet) && DeepCollectionEquality().equals(o._rules, _rules); @@ -3220,6 +3347,13 @@ class HealthContactTraceRule { ) : null; } + Map toJson() { + return { + 'duration': duration?.toJson(), + 'status': status?.toJson(), + }; + } + static List listFromJson(List json) { List values; if (json != null) { @@ -3232,6 +3366,17 @@ class HealthContactTraceRule { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthContactTraceRule value in values) { + json.add(value?.toJson()); + } + } + return json; + } + bool _matchBlob(HealthHistoryBlob blob, { HealthRulesSet rules }) { return (duration != null) && duration.match(blob?.traceDurationInMinutes, rules: rules); } @@ -3251,6 +3396,12 @@ class HealthVaccineRulesSet { ) : null; } + Map toJson() { + return { + 'rules': HealthVaccineRule.listToJson(_rules), + }; + } + bool operator ==(o) => (o is HealthVaccineRulesSet) && DeepCollectionEquality().equals(o._rules, _rules); @@ -3286,6 +3437,13 @@ class HealthVaccineRule { ) : null; } + Map toJson() { + return { + 'vaccine': vaccine, + 'status': status?.toJson(), + }; + } + bool operator ==(o) => (o is HealthVaccineRule) && (o.vaccine == vaccine) && @@ -3307,6 +3465,17 @@ class HealthVaccineRule { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthVaccineRule value in values) { + json.add(value?.toJson()); + } + } + return json; + } + bool _matchBlob(HealthHistoryBlob blob, {HealthRulesSet rules}) { return (vaccine != null) && (vaccine.toLowerCase() == blob?.vaccine?.toLowerCase()); } @@ -3326,6 +3495,12 @@ class HealthActionRulesSet { ) : null; } + Map toJson() { + return { + 'rules': HealthActionRule.listToJson(_rules), + }; + } + bool operator ==(o) => (o is HealthActionRulesSet) && DeepCollectionEquality().equals(o._rules, _rules); @@ -3361,6 +3536,13 @@ class HealthActionRule { ) : null; } + Map toJson() { + return { + 'type': type, + 'status': status?.toJson() + }; + } + bool operator ==(o) => (o is HealthActionRule) && (o.type == type) && @@ -3382,6 +3564,17 @@ class HealthActionRule { return values; } + static List listToJson(List values) { + List json; + if (values != null) { + json = []; + for (HealthActionRule value in values) { + json.add(value?.toJson()); + } + } + return json; + } + bool _matchBlob(HealthHistoryBlob blob, {HealthRulesSet rules}) { return (type != null) && (type.toLowerCase() == blob?.actionType?.toLowerCase()); } @@ -3411,6 +3604,8 @@ abstract class _HealthRuleStatus { return null; } + dynamic toJson(); + static Map mapFromJson(Map json) { Map result; if (json != null) { @@ -3423,6 +3618,17 @@ abstract class _HealthRuleStatus { return result; } + static Map mapToJson(Map values) { + Map json; + if (values != null) { + json = Map(); + values.forEach((key, value) { + json[key] = value?.toJson(); + }); + } + return json; + } + HealthRuleStatus eval({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); } @@ -3480,6 +3686,26 @@ class HealthRuleStatus extends _HealthRuleStatus { ) : null; } + @override + dynamic toJson() { + return { + 'code': code, + 'priority': priority, + 'next_step': nextStep, + 'next_step_html': nextStepHtml, + 'next_step_interval': nextStepInterval?.toJson(), + 'warning': warning, + 'warning_html': warningHtml, + 'event_explanation': eventExplanation, + 'event_explanation_html': eventExplanationHtml, + 'notice': statusUpdateNotice, + 'notice_html': statusUpdateNoticeHtml, + 'reason': statusUpdateReason, + 'reason_html': statusUpdateReasonHtml, + 'fcm_topic': fcmTopic, + }; + } + factory HealthRuleStatus.fromStatus(HealthRuleStatus status, { DateTime nextStepDateUtc, }) { return (status != null) ? HealthRuleStatus( @@ -3580,6 +3806,11 @@ class HealthRuleReferenceStatus extends _HealthRuleStatus { ) : null; } + @override + dynamic toJson() { + return reference; + } + bool operator ==(o) => (o is HealthRuleReferenceStatus) && (o.reference == reference); @@ -3614,6 +3845,16 @@ class HealthRuleConditionalStatus extends _HealthRuleStatus with HealthRuleCondi ) : null; } + @override + dynamic toJson() { + return { + 'condition': condition, + 'params': conditionParams, + 'success': successStatus?.toJson(), + 'fail': failStatus?.toJson(), + }; + } + static bool isJsonCompatible(dynamic json) { return (json is Map) && (json['condition'] is String); } @@ -3669,6 +3910,8 @@ abstract class _HealthRuleInterval { return null; } + dynamic toJson(); + bool match(int value, { DateTime orgDate, List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); int value({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); bool valid({ List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params }); @@ -3687,6 +3930,40 @@ abstract class _HealthRuleInterval { return result; } + static Map mapToJson(Map values) { + Map json; + if (values != null) { + json = Map(); + values.forEach((key, value) { + json[key] = value?.toJson(); + }); + } + return json; + } + + static List<_HealthRuleInterval> listFromJson(List json) { + List<_HealthRuleInterval> values; + if (json != null) { + values = <_HealthRuleInterval>[]; + for (dynamic entry in json) { + try { values.add(_HealthRuleInterval.fromJson(entry)); } + catch(e) { print(e?.toString()); } + } + } + return values; + } + + static List listToJson(List<_HealthRuleInterval> values) { + List json; + if (values != null) { + json = []; + for (_HealthRuleInterval value in values) { + json.add(value?.toJson()); + } + } + return json; + } + static int applyWeekdayExtent(_HealthRuleInterval weekdayExtent, DateTime orgDate, int value, int step, { List history, int historyIndex, int referenceIndex, HealthRulesSet rules, Map params } ) { if ((weekdayExtent != null) && (orgDate != null) && (value != null) && (step != null)) { //DateTime dateExt = orgDate.add(Duration(days: value + step)); @@ -3718,6 +3995,11 @@ class HealthRuleIntervalValue extends _HealthRuleInterval { return (json is int) ? HealthRuleIntervalValue(value: json) : null; } + @override + dynamic toJson() { + return _value; + } + bool operator ==(o) => (o is HealthRuleIntervalValue) && (o._value == _value); @@ -3775,6 +4057,20 @@ class HealthRuleInterval extends _HealthRuleInterval { ) : null; } + @override + dynamic toJson() { + return { + 'min': _min?.toJson(), + 'max': _max?.toJson(), + 'value': _value?.toJson(), + 'scope': _scopeToJson(_scope), + 'current': _current, + 'origin': _originToJson(_origin), + 'min-weekdays-extent': _minWeekdaysExtent?.toJson(), + 'max-weekdays-extent': _maxWeekdaysExtent?.toJson(), + }; + } + bool operator ==(o) => (o is HealthRuleInterval) && (o._min == _min) && @@ -3871,6 +4167,22 @@ class HealthRuleInterval extends _HealthRuleInterval { return null; } + static String _scopeToJson(int value) { + if (value == FutureScope) { + return 'future'; + } + else if (value == FutureAndCurrentScope) { + return 'future-and-current'; + } + else if (value == PastScope) { + return 'past'; + } + else if (value == PastAndCurrentScope) { + return 'past-and-current'; + } + return null; + } + static HealthRuleIntervalOrigin _originFromJson(dynamic value) { if (value == 'historyDate') { return HealthRuleIntervalOrigin.historyDate; @@ -3882,6 +4194,18 @@ class HealthRuleInterval extends _HealthRuleInterval { return null; } } + + static String _originToJson(HealthRuleIntervalOrigin value) { + if (value == HealthRuleIntervalOrigin.historyDate) { + return 'historyDate'; + } + else if (value == HealthRuleIntervalOrigin.referenceDate) { + return 'referenceDate'; + } + else { + return null; + } + } } /////////////////////////////// @@ -3894,19 +4218,15 @@ class HealthRuleIntervalSet extends _HealthRuleInterval { _entries = entries; factory HealthRuleIntervalSet.fromJson(List json) { - List<_HealthRuleInterval> entries; - if (json != null) { - entries = <_HealthRuleInterval>[]; - for (dynamic jsonEntry in json) { - _HealthRuleInterval entry = _HealthRuleInterval.fromJson(jsonEntry); - if (entry != null) { - entries.add(entry); - } - } - } + List<_HealthRuleInterval> entries = _HealthRuleInterval.listFromJson(json); return (entries != null) ? HealthRuleIntervalSet(entries: entries) : null; } + @override + dynamic toJson() { + return _HealthRuleInterval.listToJson(_entries); + } + bool operator ==(o) => (o is HealthRuleIntervalSet) && DeepCollectionEquality().equals(o._entries, _entries); @@ -3953,6 +4273,11 @@ class HealthRuleIntervalReference extends _HealthRuleInterval { return (json is String) ? HealthRuleIntervalReference(reference: json) : null; } + @override + dynamic toJson() { + return _reference; + } + bool operator ==(o) => (o is HealthRuleIntervalReference) && (o._reference == _reference); @@ -3997,6 +4322,16 @@ class HealthRuleIntervalCondition extends _HealthRuleInterval with HealthRuleCon ) : null; } + @override + dynamic toJson() { + return { + 'condition': condition, + 'params': conditionParams, + 'success': successInterval?.toJson(), + 'fail': failInterval?.toJson(), + }; + } + static bool isJsonCompatible(dynamic json) { return (json is Map) && (json['condition'] is String); } From 44032b5d3a84b2d5e59a8dde98772fcd7bc27711 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 8 Sep 2021 17:16:26 +0300 Subject: [PATCH 47/73] Updated CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da81843a..788c767c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Fixed +- Fixed Health model classes equality operators. +- Fixed Storage.healthUserTestMonitorInterval to be able to read null values. +### Added +- Added HealthRulesSet.toJson() getter. ## [2.10.37] - 2021-09-07 ### Changed From 68c5e85d13b1e49b3b68b5999ad7f5265de6e40b Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 8 Sep 2021 17:20:15 +0300 Subject: [PATCH 48/73] version: 2.10.38+1038 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 788c767c..f7c84ca2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.10.38] - 2021-09-08 ### Fixed - Fixed Health model classes equality operators. - Fixed Storage.healthUserTestMonitorInterval to be able to read null values. diff --git a/pubspec.yaml b/pubspec.yaml index acd8c6bf..094aebe5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.37+1037 +version: 2.10.38+1038 environment: sdk: ">=2.2.0 <3.0.0" From 9a66a712c2059363f9b50149ba2ed348a95383b4 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 10 Sep 2021 10:48:44 +0300 Subject: [PATCH 49/73] version: 2.11.0+1100 (#706) --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c84ca2..3a311b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.11.0] - 2021-09-10 + ## [2.10.38] - 2021-09-08 ### Fixed - Fixed Health model classes equality operators. diff --git a/pubspec.yaml b/pubspec.yaml index 094aebe5..861b69ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.10.38+1038 +version: 2.11.0+1100 environment: sdk: ">=2.2.0 <3.0.0" From 1faf9af8bf5e9c746906d8a4484ada910763741a Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 10 Sep 2021 11:09:49 +0300 Subject: [PATCH 50/73] "Feedback" section in Settings Home panel is "Get Help" now (#705). --- assets/flexUI.json | 2 +- assets/strings.en.json | 10 ++-- assets/strings.es.json | 10 ++-- assets/strings.ja.json | 10 ++-- assets/strings.zh.json | 10 ++-- lib/service/Config.dart | 2 +- lib/ui/settings/SettingsHomePanel.dart | 64 +++++++++++--------------- 7 files changed, 44 insertions(+), 64 deletions(-) diff --git a/assets/flexUI.json b/assets/flexUI.json index 7d22bf38..d869212e 100644 --- a/assets/flexUI.json +++ b/assets/flexUI.json @@ -11,7 +11,7 @@ "home.your_health" : [ "health_status", "tiles", "health_history", "find_test_location", "wellness_center", "_groups", "switch_account"], "home.your_health.tiles": ["county_guidelines", "care_team"], - "settings": ["user_info", "connect", "customizations", "connected", "notifications", "covid19", "privacy", "account", "feedback"], + "settings": ["user_info", "connect", "customizations", "connected", "notifications", "covid19", "privacy", "account", "get_help"], "settings.connect": ["netid", "phone"], "settings.customizations": ["roles"], "settings.connected": ["netid", "phone"], diff --git a/assets/strings.en.json b/assets/strings.en.json index 94930620..2eee67d0 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -164,11 +164,11 @@ "panel.onboarding.base.not_now.hint": "", "panel.onboarding.base.not_now.title": "Not right now", - "panel.settings.feedback.label.title": "Provide Feedback", + "panel.settings.get_help.label.title": "Get Help", "panel.settings.privacy_statement.label.title": "Privacy Statement", - "panel.settings.label.offline.feedback": "Providing a feedback is not available while offline.", + "panel.settings.label.offline.get_help": "Getting a help is not available while offline.", "panel.settings.home.settings.header": "Settings", "panel.settings.home.connect.not_logged_in.title": "Connect to Illinois", @@ -213,10 +213,8 @@ "panel.settings.home.button.debug.hint": "", "panel.settings.home.button.test.title": "Test", "panel.settings.home.button.test.hint": "", - "panel.settings.home.feedback.title": "We need your ideas!", - "panel.settings.home.feedback.description": "Enjoying the app? Missing something? Tap on the bottom to submit your idea.", - "panel.settings.home.button.feedback.title": "Submit Feedback", - "panel.settings.home.button.feedback.hint": "", + "panel.settings.home.button.get_help.title": "Get Help", + "panel.settings.home.button.get_help.hint": "", "panel.settings.home.covid19.exposure_notifications": "Exposure Notifications", "panel.settings.home.covid19.provider_test_result": "Health Provider Test Results", "panel.settings.home.covid19.provider_vaccine_info": "Health Provider Vaccine Information", diff --git a/assets/strings.es.json b/assets/strings.es.json index 070786d3..a1958325 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -164,11 +164,11 @@ "panel.onboarding.base.not_now.hint": "", "panel.onboarding.base.not_now.title": "No en este momento", - "panel.settings.feedback.label.title": "Proporcionar comentarios", + "panel.settings.get_help.label.title": "Consigue Ayuda", "panel.settings.privacy_statement.label.title": "Declaración de privacidad", - "panel.settings.label.offline.feedback": "Proporcionar un comentario no está disponible sin conexión.", + "panel.settings.label.offline.get_help": "No es posible obtener ayuda sin conexión.", "panel.settings.home.settings.header": "Configuración", "panel.settings.home.connect.not_logged_in.title": "Conéctate a Illinois", @@ -213,10 +213,8 @@ "panel.settings.home.button.debug.hint": "", "panel.settings.home.button.test.title": "Prueba", "panel.settings.home.button.test.hint": "", - "panel.settings.home.feedback.title": "¡Necesitamos tus ideas!", - "panel.settings.home.feedback.description": "¿Estás disfrutando la aplicación? ¿Echando de menos algo? Toque en la parte inferior para enviar su idea", - "panel.settings.home.button.feedback.title": "Enviar comentarios", - "panel.settings.home.button.feedback.hint": "", + "panel.settings.home.button.get_help.title": "Consigue Ayuda", + "panel.settings.home.button.get_help.hint": "", "panel.settings.home.covid19.exposure_notifications": "Notificaciones de exposición", "panel.settings.home.covid19.provider_test_result": "Resultados de la prueba del proveedor de salud", "panel.settings.home.covid19.provider_vaccine_info": "Información sobre vacunas del proveedor de salud", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index f8f94d23..61c70168 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -164,11 +164,11 @@ "panel.onboarding.base.not_now.hint": "", "panel.onboarding.base.not_now.title": "今はしない", - "panel.settings.feedback.label.title": "Provide Feedback", + "panel.settings.get_help.label.title": "得到幫助", "panel.settings.privacy_statement.label.title": "Privacy Statement", - "panel.settings.label.offline.feedback": "Providing a feedback is not available while offline.", + "panel.settings.label.offline.get_help": "オフライン中はヘルプを利用できません。", "panel.settings.home.settings.header": "設定", "panel.settings.home.connect.not_logged_in.title": "Connect to Illinois", @@ -213,10 +213,8 @@ "panel.settings.home.button.debug.hint": "", "panel.settings.home.button.test.title": "Test", "panel.settings.home.button.test.hint": "", - "panel.settings.home.feedback.title": "We need your ideas!", - "panel.settings.home.feedback.description": "Enjoying the app? Missing something? Tap on the bottom to submit your idea.", - "panel.settings.home.button.feedback.title": "Submit Feedback", - "panel.settings.home.button.feedback.hint": "", + "panel.settings.home.button.get_help.title": "助けを得ます", + "panel.settings.home.button.get_help.hint": "", "panel.settings.home.covid19.exposure_notifications": "接続通知", "panel.settings.home.covid19.provider_test_result": "医療提供者のテスト結果", "panel.settings.home.covid19.provider_vaccine_info": "医療提供者のワクチン情報", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index e9918651..beb7f284 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -164,11 +164,11 @@ "panel.onboarding.base.not_now.hint": "", "panel.onboarding.base.not_now.title": "暂时不", - "panel.settings.feedback.label.title": "提供反馈", + "panel.settings.get_help.label.title": "得到幫助", "panel.settings.privacy_statement.label.title": "隐私声明", - "panel.settings.label.offline.feedback": "离线时无法提供反馈。", + "panel.settings.label.offline.get_help": "離線時無法獲得幫助。", "panel.settings.home.settings.header": "设置", "panel.settings.home.connect.not_logged_in.title": "連接到伊利諾伊州", @@ -213,10 +213,8 @@ "panel.settings.home.button.debug.hint": "", "panel.settings.home.button.test.title": "测试", "panel.settings.home.button.test.hint": "", - "panel.settings.home.feedback.title": " 我们需要你的主意!", - "panel.settings.home.feedback.description": " 喜欢这个应用程序吗?我们遗漏了什么?点击底部提交您的想法。", - "panel.settings.home.button.feedback.title": "提交反馈", - "panel.settings.home.button.feedback.hint": "", + "panel.settings.home.button.get_help.title": "得到幫助", + "panel.settings.home.button.get_help.hint": "", "panel.settings.home.covid19.exposure_notifications": "接触通知", "panel.settings.home.covid19.provider_test_result": "健康提供者測試結果", "panel.settings.home.covid19.provider_vaccine_info": "衛生提供者疫苗信息", diff --git a/lib/service/Config.dart b/lib/service/Config.dart index 3148d6d3..1b79719c 100644 --- a/lib/service/Config.dart +++ b/lib/service/Config.dart @@ -359,7 +359,7 @@ class Config with Service implements NotificationsListener { Map get onboardingInfo { return (_config != null) ? (_config['onboarding'] ?? {}) : {}; } String get assetsUrl { return otherUniversityServices['assets_url']; } // "https://rokwire-assets.s3.us-east-2.amazonaws.com" - String get feedbackUrl { return otherUniversityServices['feedback_url']; } // "https://forms.illinois.edu/sec/1971889" + String get getHelpUrl { return otherUniversityServices['get_help_url']; } // "https://forms.illinois.edu/sec/4961936" String get iCardUrl { return otherUniversityServices['icard_url']; } // "https://www.icard.uillinois.edu/rest/rw/rwIDData/rwCardInfo" String get privacyPolicyUrl { return otherUniversityServices['privacy_policy_url']; } // "https://www.vpaa.uillinois.edu/resources/web_privacy" String get exposureLogUrl { return otherUniversityServices['exposure_log_url']; } // "http://ec2-18-191-37-235.us-east-2.compute.amazonaws.com:8003/PostSessionData" diff --git a/lib/ui/settings/SettingsHomePanel.dart b/lib/ui/settings/SettingsHomePanel.dart index 2b53e3f8..04d57bbc 100644 --- a/lib/ui/settings/SettingsHomePanel.dart +++ b/lib/ui/settings/SettingsHomePanel.dart @@ -160,8 +160,8 @@ class _SettingsHomePanelState extends State implements Notifi else if (code == 'account') { contentList.add(_buildAccount()); } - else if (code == 'feedback') { - contentList.add(_buildFeedback(),); + else if (code == 'get_help') { + contentList.add(_buildGetHelp(),); } } @@ -1139,55 +1139,40 @@ class _SettingsHomePanelState extends State implements Notifi } } - // Feedback + // Get Help - Widget _buildFeedback(){ + Widget _buildGetHelp(){ return Column( children: [ Padding( - padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - Localization().getStringEx("panel.settings.home.feedback.title", "We need your ideas!"), - style: TextStyle(color: Styles().colors.fillColorPrimary, fontSize: 20), - ), - Container(height: 5,), - Text( - Localization().getStringEx("panel.settings.home.feedback.description", "Enjoying the app? Missing something? Tap on the bottom to submit your idea."), - style: TextStyle(fontFamily: Styles().fontFamilies.regular,color: Styles().colors.textBackground, fontSize: 16), - ), - ]) - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12), + padding: EdgeInsets.only(left: 18, right: 18, top: 24, bottom: 6), child: ScalableRoundedButton( - label: Localization().getStringEx("panel.settings.home.button.feedback.title", "Submit Feedback"), - hint: Localization().getStringEx("panel.settings.home.button.feedback.hint", ""), + label: Localization().getStringEx("panel.settings.home.button.get_help.title", "Get Help"), + hint: Localization().getStringEx("panel.settings.home.button.get_help.hint", ""), backgroundColor: Styles().colors.background, fontSize: 16.0, textColor: Styles().colors.fillColorPrimary, borderColor: Styles().colors.fillColorSecondary, showExternalLink: true, - onTap: _onFeedbackClicked, + onTap: _onGetHelpClicked, ), ), ], ); } - void _onFeedbackClicked() { + void _onGetHelpClicked() { if (Connectivity().isNotOffline) { - Analytics.instance.logSelect(target: "Provide Feedback"); + Analytics.instance.logSelect(target: "Get Help"); - if (Connectivity().isNotOffline && (Config().feedbackUrl != null)) { - String feedbackUrl = Config().feedbackUrl; - - String panelTitle = Localization().getStringEx('panel.settings.feedback.label.title', 'PROVIDE FEEDBACK'); - Navigator.push( - context, CupertinoPageRoute(builder: (context) => WebPanel(url: feedbackUrl, title: panelTitle,))); + if (Connectivity().isNotOffline && (Config().getHelpUrl != null)) { + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel( + url: Config().getHelpUrl, + title: Localization().getStringEx('panel.settings.get_help.label.title', 'Get Help'),) + )); } else { - AppAlert.showOfflineMessage(context, Localization().getStringEx('panel.settings.label.offline.feedback', 'Providing a Feedback is not available while offline.')); + AppAlert.showOfflineMessage(context, Localization().getStringEx('panel.settings.label.offline.get_help', 'Getting a help is not available while offline.')); } } else { AppAlert.showOfflineMessage(context); @@ -1198,7 +1183,7 @@ class _SettingsHomePanelState extends State implements Notifi Widget _buildDebug() { return Padding( - padding: EdgeInsets.symmetric(horizontal: 18, vertical: 24), + padding: EdgeInsets.only(left: 18, right: 18, top: 6, bottom: 6), child: ScalableRoundedButton( label: Localization().getStringEx("panel.profile_info.button.debug.title", "Debug"), hint: Localization().getStringEx("panel.profile_info.button.debug.hint", ""), @@ -1231,12 +1216,15 @@ class _SettingsHomePanelState extends State implements Notifi // Version Info Widget _buildVersionInfo(){ - return Container( - alignment: Alignment.center, - child: Text( - "Version: $_versionName", - style: TextStyle(color: Styles().colors.textBackground, fontFamily: Styles().fontFamilies.regular, fontSize: 16), - )); + return Padding( + padding: EdgeInsets.only(left: 18, right: 18, top: 6, bottom: 6), + child: Container( + alignment: Alignment.center, + child: Text( + "Version: $_versionName", + style: TextStyle(color: Styles().colors.textBackground, fontFamily: Styles().fontFamilies.regular, fontSize: 16), + )) + ); } void _loadVersionInfo() async { From f5c703e3166a0f432cb12870f2f7b1ba2dcee84a Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 10 Sep 2021 11:18:19 +0300 Subject: [PATCH 51/73] Updated CHANGELOG.md (#705). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a311b7f..2fe0c009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ## [2.11.0] - 2021-09-10 +### Changed +- "Feedback" section in Settings Home panel changed to "Get Help" [#705](https://github.com/rokwire/safer-illinois-app/issues/705). ## [2.10.38] - 2021-09-08 ### Fixed From 9db39f710f4e213cf534f60fa796d12f03f75481 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 23 Sep 2021 12:17:45 +0300 Subject: [PATCH 52/73] Updated vaccination widget strings (#708). --- lib/ui/health/HealthHomePanel.dart | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 06577fcb..0e882bb1 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -707,11 +707,10 @@ class _HealthHomePanelState extends State implements Notificati if (lastVaccineTaken == null) { statusTitleText = 'Get a vaccine now'; statusDescriptionText = """ -• COVID-19 vaccines are safe. -• COVID-19 vaccines are effective. -• Once you are fully vaccinated, you can start doing more. -• COVID-19 vaccination is a safer way to help build protection. -• None of the COVID-19 vaccines can make you sick with COVID-19."""; +• COVID-19 vaccines are safe +• COVID-19 vaccines are effective +• COVID-19 vaccines allow you to safely do more +• COVID-19 vaccines build safer protection"""; } else if (numberOfTakenVaccines == 1) { headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; @@ -719,8 +718,8 @@ class _HealthHomePanelState extends State implements Notificati DateTime nextDoseDate = lastVaccineTaken?.dateUtc?.add(Duration(days: 21)); String nextDoseDateStr = AppDateTime.formatDateTime(nextDoseDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - statusTitleText = 'Get your second vaccination'; - statusDescriptionText = "Get your second dose of vaccine on $nextDoseDateStr."; + statusTitleText = 'Таке second vaccination'; + statusDescriptionText = "Таке your second dose of vaccine on $nextDoseDateStr."; } else { headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; @@ -808,7 +807,7 @@ class _HealthHomePanelState extends State implements Notificati borderColor: Styles().colors.fillColorSecondary, backgroundColor: Styles().colors.surface, textColor: Styles().colors.fillColorPrimary, - onTap: _onTapFindVaccineAppointment, + onTap: _onTapMakeVaccineAppointment, )), ) ]); @@ -1080,10 +1079,10 @@ class _HealthHomePanelState extends State implements Notificati } } - void _onTapFindVaccineAppointment() { + void _onTapMakeVaccineAppointment() { if (Connectivity().isNotOffline) { - Analytics.instance.logSelect(target: "COVID-19 Find Vaccine Appointment"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://mymckinley.illinois.edu'))); + Analytics.instance.logSelect(target: "COVID-19 Make Vaccine Appointment"); + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://www.vaccines.gov'))); } else { AppAlert.showOfflineMessage(context); } From 28482003e41947d52be1c3c3eba9282d76905659 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 23 Sep 2021 13:58:00 +0300 Subject: [PATCH 53/73] Vaccination widget: load strings from app resources, load vaccine periods and appointment url from app config (#708). --- assets/strings.en.json | 21 +++++++++++ assets/strings.es.json | 21 +++++++++++ assets/strings.ja.json | 21 +++++++++++ assets/strings.zh.json | 21 +++++++++++ lib/service/Config.dart | 5 +-- lib/ui/health/HealthHomePanel.dart | 57 +++++++++++++++++++----------- lib/utils/Utils.dart | 18 ++++++++++ 7 files changed, 142 insertions(+), 22 deletions(-) diff --git a/assets/strings.en.json b/assets/strings.en.json index 2eee67d0..ccc5b9fe 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -336,6 +336,16 @@ "panel.covid19home.button.groups.title": "Groups", "panel.covid19home.button.groups.hint": "", + "panel.covid19home.vaccination.heading.title": "VACCINATION", + "panel.covid19home.vaccination.none.title": "Get a vaccine now", + "panel.covid19home.vaccination.none.description": "• COVID-19 vaccines are safe\n• COVID-19 vaccines are effective\n• COVID-19 vaccines allow you to safely do more\n• COVID-19 vaccines build safer protection", + "panel.covid19home.vaccination.progress.title": "Get %s vaccination", + "panel.covid19home.vaccination.progress.description": "Get your %s dose of vaccine on %s.", + "panel.covid19home.vaccination.finished.title": "Wait for vaccination to get effective", + "panel.covid19home.vaccination.finished.description": "Your vaccination will become effective after %s.", + "panel.covid19home.vaccination.button.appointment.title": "Make vaccination appointment", + "panel.covid19home.vaccination.button.appointment.hint": "", + "panel.covid19_test_locations.header.title": "Test Locations", "panel.covid19_test_locations.label.contact.title": "Contact", "panel.covid19_test_locations.distance.text": "mi away", @@ -899,6 +909,17 @@ "model.explore.time.at": "at", "model.explore.time.all_day": "All Day", + "model.number.ordinal.long.1": "first", + "model.number.ordinal.long.2": "second", + "model.number.ordinal.long.3": "third", + "model.number.ordinal.long.4": "fourth", + "model.number.ordinal.long.5": "fifth", + "model.number.ordinal.long.6": "sixth", + "model.number.ordinal.long.7": "seventh", + "model.number.ordinal.long.8": "eighth", + "model.number.ordinal.long.9": "ninth", + "model.number.ordinal.long.10": "tenth", + "logic.date_time.greeting.morning": "Good morning", "logic.date_time.greeting.afternoon": "Good afternoon", "logic.date_time.greeting.evening": "Good evening", diff --git a/assets/strings.es.json b/assets/strings.es.json index a1958325..c902c2f0 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -336,6 +336,16 @@ "panel.covid19home.button.groups.title": "Groups", "panel.covid19home.button.groups.hint": "", + "panel.covid19home.vaccination.heading.title": "VACUNACIÓN", + "panel.covid19home.vaccination.none.title": "Obtenga una vacuna ahora", + "panel.covid19home.vaccination.none.description": "• Las vacunas COVID-19 son seguras\n• Las vacunas COVID-19 son efectivas\n• Las vacunas COVID-19 le permiten hacer más de manera segura\n• Las vacunas COVID-19 crean una protección más segura", + "panel.covid19home.vaccination.progress.title": "Recibe la vacuna %s", + "panel.covid19home.vaccination.progress.description": "Obtenga su %s dosis de vacuna el %s.", + "panel.covid19home.vaccination.finished.title": "Espere a que la vacunación sea efectiva", + "panel.covid19home.vaccination.finished.description": "Su vacuna será efectiva después de %s.", + "panel.covid19home.vaccination.button.appointment.title": "Concierte una cita de vacunación", + "panel.covid19home.vaccination.button.appointment.hint": "", + "panel.covid19_test_locations.header.title": "Lugares de prueba", "panel.covid19_test_locations.label.contact.title": "Contacto", "panel.covid19_test_locations.distance.text": "mi distancia", @@ -895,6 +905,17 @@ "model.explore.time.at": "a", "model.explore.time.all_day": "Todo el dia", + "model.number.ordinal.long.1": "primera", + "model.number.ordinal.long.2": "segunda", + "model.number.ordinal.long.3": "Tercera", + "model.number.ordinal.long.4": "Cuarta", + "model.number.ordinal.long.5": "quinta", + "model.number.ordinal.long.6": "sexta", + "model.number.ordinal.long.7": "séptima", + "model.number.ordinal.long.8": "octava", + "model.number.ordinal.long.9": "novena", + "model.number.ordinal.long.10": "décima", + "logic.date_time.greeting.morning": "Buenos días", "logic.date_time.greeting.afternoon": "Buenas tardes", "logic.date_time.greeting.evening": "Buena noches", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index 61c70168..89203f89 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -336,6 +336,16 @@ "panel.covid19home.button.groups.title": "グループ", "panel.covid19home.button.groups.hint": "", + "panel.covid19home.vaccination.heading.title": "ワクチン", + "panel.covid19home.vaccination.none.title": "今すぐワクチンを入手", + "panel.covid19home.vaccination.none.description": "• COVID-19 疫苗是安全的\n• COVID-19ワクチンは効果的です\n• COVID-19ワクチンはあなたが安全にもっと多くのことをすることを可能にします\n• COVID-19ワクチンはより安全な保護を構築します", + "panel.covid19home.vaccination.progress.title": "%s予防接種を受ける", + "panel.covid19home.vaccination.progress.description": "%sでワクチンの%s投与量を取得します。", + "panel.covid19home.vaccination.finished.title": "予防接種が効果を発揮するのを待つ", + "panel.covid19home.vaccination.finished.description": "予防接種は%s後に有効になります。", + "panel.covid19home.vaccination.button.appointment.title": "予防接種の予約をする", + "panel.covid19home.vaccination.button.appointment.hint": "", + "panel.covid19_test_locations.header.title": "検査現場", "panel.covid19_test_locations.label.contact.title": "Contact", "panel.covid19_test_locations.distance.text": "mi away", @@ -899,6 +909,17 @@ "model.explore.time.at": "at", "model.explore.time.all_day": "一日中", + "model.number.ordinal.long.1": "初め", + "model.number.ordinal.long.2": "2番目", + "model.number.ordinal.long.3": "三番目", + "model.number.ordinal.long.4": "第4", + "model.number.ordinal.long.5": "5番目", + "model.number.ordinal.long.6": "6番目", + "model.number.ordinal.long.7": "7番目", + "model.number.ordinal.long.8": "第8", + "model.number.ordinal.long.9": "9番目", + "model.number.ordinal.long.10": "10番目", + "logic.date_time.greeting.morning": "おはようございます。", "logic.date_time.greeting.afternoon": "こんにちは", "logic.date_time.greeting.evening": "こんばんは", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index beb7f284..04755ce2 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -336,6 +336,16 @@ "panel.covid19home.button.groups.title": "Groups", "panel.covid19home.button.groups.hint": "", + "panel.covid19home.vaccination.heading.title": "疫苗接種", + "panel.covid19home.vaccination.none.title": "立即接種疫苗", + "panel.covid19home.vaccination.none.description": "• COVID-19 疫苗是安全的\n• COVID-19 疫苗是有效的\n• COVID-19 疫苗可讓您安全地做更多事情\n• COVID-19 疫苗可建立更安全的保護", + "panel.covid19home.vaccination.progress.title": "接種%s疫苗", + "panel.covid19home.vaccination.progress.description": "在%s上接種%s劑量的疫苗。", + "panel.covid19home.vaccination.finished.title": "等待疫苗接種生效", + "panel.covid19home.vaccination.finished.description": "您的疫苗接種將在%s後生效。", + "panel.covid19home.vaccination.button.appointment.title": "預約接種", + "panel.covid19home.vaccination.button.appointment.hint": "", + "panel.covid19_test_locations.header.title": "测试位置", "panel.covid19_test_locations.label.contact.title": "联系人", "panel.covid19_test_locations.distance.text": "英里遠", @@ -899,6 +909,17 @@ "model.explore.time.at": "在", "model.explore.time.all_day": "一整天", + "model.number.ordinal.long.1": "第一的", + "model.number.ordinal.long.2": "第二", + "model.number.ordinal.long.3": "第三", + "model.number.ordinal.long.4": "第四", + "model.number.ordinal.long.5": "第五", + "model.number.ordinal.long.6": "第六", + "model.number.ordinal.long.7": "第七", + "model.number.ordinal.long.8": "第八", + "model.number.ordinal.long.9": "第九", + "model.number.ordinal.long.10": "第十", + "logic.date_time.greeting.morning": "早上好", "logic.date_time.greeting.afternoon": "下午好", "logic.date_time.greeting.evening": "晚上好", diff --git a/lib/service/Config.dart b/lib/service/Config.dart index 1b79719c..5ea61268 100644 --- a/lib/service/Config.dart +++ b/lib/service/Config.dart @@ -384,6 +384,7 @@ class Config with Service implements NotificationsListener { String get imagesServiceUrl { return platformBuildingBlocks['images_service_url']; } // "https://api-dev.rokwire.illinois.edu/images-service"; String get osfBaseUrl { return thirdPartyServices['osf_base_url']; } // "https://ssproxy.osfhealthcare.org/fhir-proxy" + String get vaccinationAppointUrl { return thirdPartyServices['vaccination_appointment_url']; } // "https://ssproxy.osfhealthcare.org/fhir-proxy" String get rokwireApiKey { return secretRokwire['api_key']; } @@ -397,8 +398,8 @@ class Config with Service implements NotificationsListener { int get refreshTimeout { return kReleaseMode ? (settings['refreshTimeout'] ?? 0) : 0; } - bool get residentRoleEnabled { return false; } - bool get capitolStaffRoleEnabled { return (settings['roleCapitolStaffEnabled'] == true); } + bool get residentRoleEnabled { return false; } + bool get capitolStaffRoleEnabled { return (settings['roleCapitolStaffEnabled'] == true); } } diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 0e882bb1..3b9e8c02 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -22,6 +22,7 @@ import 'package:flutter_html/style.dart'; import 'package:illinois/model/Health.dart'; import 'package:illinois/service/Analytics.dart'; import 'package:illinois/service/Auth.dart'; +import 'package:illinois/service/Config.dart'; import 'package:illinois/service/FlexUI.dart'; import 'package:illinois/service/Health.dart'; import 'package:illinois/service/Organizations.dart'; @@ -48,6 +49,7 @@ import 'package:illinois/ui/widgets/RoundedButton.dart'; import 'package:illinois/ui/widgets/SectionTitlePrimary.dart'; import 'package:illinois/ui/widgets/StatusInfoDialog.dart'; import 'package:illinois/utils/Utils.dart'; +import 'package:sprintf/sprintf.dart'; import 'package:url_launcher/url_launcher.dart'; class HealthHomePanel extends StatefulWidget { @@ -684,9 +686,10 @@ class _HealthHomePanelState extends State implements Notificati HealthHistory lastVaccineTaken; int numberOfTakenVaccines = 0; + DateTime nowUtc = DateTime.now().toUtc(); for (HealthHistory historyEntry in Health().history ?? []) { - if (historyEntry.isVaccine) { + if ((historyEntry.dateUtc != null) && historyEntry.dateUtc.isBefore(nowUtc) && historyEntry.isVaccine) { if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineEffective?.toLowerCase()) { // 5.2.4 When effective then hide the widget return null; @@ -700,35 +703,47 @@ class _HealthHomePanelState extends State implements Notificati } } - String headingTitle = 'VACCINATION', headingDate; + String headingTitle = Localization().getStringEx('panel.covid19home.vaccination.heading.title', 'VACCINATION'); + String headingDate; String statusTitleText, statusTitleHtml; String statusDescriptionText, statusDescriptionHtml; + + //TMP: List vaccinePeriods = AppJson.listIntValue(Config().settings['covid19VaccinePeriods']); + ListvaccinePeriods = [21, 14]; if (lastVaccineTaken == null) { - statusTitleText = 'Get a vaccine now'; - statusDescriptionText = """ + // No vaccine taken - promote it. + statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.none.title', 'Get a vaccine now'); + statusDescriptionText = Localization().getStringEx('panel.covid19home.vaccination.none.description', """ • COVID-19 vaccines are safe • COVID-19 vaccines are effective • COVID-19 vaccines allow you to safely do more -• COVID-19 vaccines build safer protection"""; +• COVID-19 vaccines build safer protection"""); + } + else if (vaccinePeriods == null) { + // Disable further processing if no periods defined. + return null; } - else if (numberOfTakenVaccines == 1) { + else if (numberOfTakenVaccines < vaccinePeriods.length) { + // There is a next vaccine to to take: show when. headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - DateTime nextDoseDate = lastVaccineTaken?.dateUtc?.add(Duration(days: 21)); + int nextDoseOffset = vaccinePeriods[numberOfTakenVaccines - 1]; + DateTime nextDoseDate = lastVaccineTaken?.dateUtc?.add(Duration(days: nextDoseOffset)); String nextDoseDateStr = AppDateTime.formatDateTime(nextDoseDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - statusTitleText = 'Таке second vaccination'; - statusDescriptionText = "Таке your second dose of vaccine on $nextDoseDateStr."; + String ordinal = AppString.localizedOrdinal(numberOfTakenVaccines + 1) ?? '$numberOfTakenVaccines-th'; + statusTitleText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.progress.title', 'Get %s vaccination'), [ordinal]); + statusDescriptionText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.progress.description', 'Get your %s dose of vaccine on %s.'), [ordinal, nextDoseDateStr]); } else { headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - DateTime vaccineEffectiveDate = lastVaccineTaken?.dateUtc?.add(Duration(days: 14)); + DateTime vaccineEffectiveDate = lastVaccineTaken?.dateUtc?.add(Duration(days: vaccinePeriods.last)); String vaccineEffectiveDateStr = AppDateTime.formatDateTime(vaccineEffectiveDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - statusTitleText = 'Wait for vaccination to get effective'; - statusDescriptionText = "Your vaccination will become effective after $vaccineEffectiveDateStr."; + statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.finished.title', 'Wait for vaccination to get effective'); + statusDescriptionText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.finished.description', 'Your vaccination will become effective after %s.'), [vaccineEffectiveDateStr]); } List contentWidgets = [ @@ -795,15 +810,15 @@ class _HealthHomePanelState extends State implements Notificati ],), ]; - if (numberOfTakenVaccines < 2) { + if ((numberOfTakenVaccines < vaccinePeriods.length) && (Config().vaccinationAppointUrl != null)) { contentList.addAll([ Container(margin: EdgeInsets.only(top: 14, bottom: 14), height: 1, color: Styles().colors.fillColorPrimaryTransparent015,), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Semantics(explicitChildNodes: true, child: ScalableRoundedButton( - label: "Make vaccination appointment", - hint: "", + label: Localization().getStringEx('panel.covid19home.vaccination.button.appointment.title', 'Make vaccination appointment'), + hint: Localization().getStringEx('panel.covid19home.vaccination.button.appointment.hint', ''), borderColor: Styles().colors.fillColorSecondary, backgroundColor: Styles().colors.surface, textColor: Styles().colors.fillColorPrimary, @@ -1080,11 +1095,13 @@ class _HealthHomePanelState extends State implements Notificati } void _onTapMakeVaccineAppointment() { - if (Connectivity().isNotOffline) { - Analytics.instance.logSelect(target: "COVID-19 Make Vaccine Appointment"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://www.vaccines.gov'))); - } else { - AppAlert.showOfflineMessage(context); + if (Config().vaccinationAppointUrl != null) { + if (Connectivity().isNotOffline) { + Analytics.instance.logSelect(target: "COVID-19 Make Vaccine Appointment"); + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://www.vaccines.gov'))); + } else { + AppAlert.showOfflineMessage(context); + } } } diff --git a/lib/utils/Utils.dart b/lib/utils/Utils.dart index 5f1ab617..db9dfc85 100644 --- a/lib/utils/Utils.dart +++ b/lib/utils/Utils.dart @@ -109,6 +109,12 @@ class AppString { } return null; } + + static List _defaultLongOrdinals = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth']; + + static String localizedOrdinal(int value) { + return ((1 <= value) && (value <= _defaultLongOrdinals.length)) ? Localization().getStringEx('model.number.ordinal.long.$value', _defaultLongOrdinals[value - 1]) : null; + } } class AppCollection { @@ -328,6 +334,18 @@ class AppJson { catch(e) { print(e?.toString()); } return null; } + + static List listStringValue(dynamic value) { + try { return (value is List) ? value.cast() : null; } + catch(e) { print(e?.toString()); } + return null; + } + + static List listIntValue(dynamic value) { + try { return (value is List) ? value.cast() : null; } + catch(e) { print(e?.toString()); } + return null; + } } class AppFile { From 544332a1eb66c924b4a1b830c6e29cab2a15e2f9 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 23 Sep 2021 14:00:31 +0300 Subject: [PATCH 54/73] Updated CHANGELOG.md (#708). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe0c009..cf643e79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Updated vaccination widget strings, load related values and URL from app config [#708](https://github.com/rokwire/safer-illinois-app/issues/708). ## [2.11.0] - 2021-09-10 ### Changed From 1821956aa2a73b163dbf47ed556868d0fe5e2135 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Thu, 23 Sep 2021 14:38:04 +0300 Subject: [PATCH 55/73] version: 2.11.1+1101 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf643e79..fbf05f64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.11.1] - 2021-09-23 ### Changed - Updated vaccination widget strings, load related values and URL from app config [#708](https://github.com/rokwire/safer-illinois-app/issues/708). diff --git a/pubspec.yaml b/pubspec.yaml index 861b69ce..eb74607e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.11.0+1100 +version: 2.11.1+1101 environment: sdk: ">=2.2.0 <3.0.0" From 79d7c2dbfe7dbcc1cad0ddabbff9f4d27cf877fb Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 24 Sep 2021 09:59:34 +0300 Subject: [PATCH 56/73] Changed vaccination appointment text (#708). --- assets/strings.en.json | 2 +- assets/strings.es.json | 2 +- assets/strings.ja.json | 2 +- assets/strings.zh.json | 2 +- lib/ui/health/HealthHomePanel.dart | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/strings.en.json b/assets/strings.en.json index ccc5b9fe..72d94b35 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -343,7 +343,7 @@ "panel.covid19home.vaccination.progress.description": "Get your %s dose of vaccine on %s.", "panel.covid19home.vaccination.finished.title": "Wait for vaccination to get effective", "panel.covid19home.vaccination.finished.description": "Your vaccination will become effective after %s.", - "panel.covid19home.vaccination.button.appointment.title": "Make vaccination appointment", + "panel.covid19home.vaccination.button.appointment.title": "Make an appointment", "panel.covid19home.vaccination.button.appointment.hint": "", "panel.covid19_test_locations.header.title": "Test Locations", diff --git a/assets/strings.es.json b/assets/strings.es.json index c902c2f0..c9a5fc1a 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -343,7 +343,7 @@ "panel.covid19home.vaccination.progress.description": "Obtenga su %s dosis de vacuna el %s.", "panel.covid19home.vaccination.finished.title": "Espere a que la vacunación sea efectiva", "panel.covid19home.vaccination.finished.description": "Su vacuna será efectiva después de %s.", - "panel.covid19home.vaccination.button.appointment.title": "Concierte una cita de vacunación", + "panel.covid19home.vaccination.button.appointment.title": "Haga una cita", "panel.covid19home.vaccination.button.appointment.hint": "", "panel.covid19_test_locations.header.title": "Lugares de prueba", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index 89203f89..378fc60b 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -343,7 +343,7 @@ "panel.covid19home.vaccination.progress.description": "%sでワクチンの%s投与量を取得します。", "panel.covid19home.vaccination.finished.title": "予防接種が効果を発揮するのを待つ", "panel.covid19home.vaccination.finished.description": "予防接種は%s後に有効になります。", - "panel.covid19home.vaccination.button.appointment.title": "予防接種の予約をする", + "panel.covid19home.vaccination.button.appointment.title": "予約する", "panel.covid19home.vaccination.button.appointment.hint": "", "panel.covid19_test_locations.header.title": "検査現場", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index 04755ce2..eeb9327e 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -343,7 +343,7 @@ "panel.covid19home.vaccination.progress.description": "在%s上接種%s劑量的疫苗。", "panel.covid19home.vaccination.finished.title": "等待疫苗接種生效", "panel.covid19home.vaccination.finished.description": "您的疫苗接種將在%s後生效。", - "panel.covid19home.vaccination.button.appointment.title": "預約接種", + "panel.covid19home.vaccination.button.appointment.title": "預約", "panel.covid19home.vaccination.button.appointment.hint": "", "panel.covid19_test_locations.header.title": "测试位置", diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 3b9e8c02..05fbb404 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -817,7 +817,7 @@ class _HealthHomePanelState extends State implements Notificati Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Semantics(explicitChildNodes: true, child: ScalableRoundedButton( - label: Localization().getStringEx('panel.covid19home.vaccination.button.appointment.title', 'Make vaccination appointment'), + label: Localization().getStringEx('panel.covid19home.vaccination.button.appointment.title', 'Make an appointment'), hint: Localization().getStringEx('panel.covid19home.vaccination.button.appointment.hint', ''), borderColor: Styles().colors.fillColorSecondary, backgroundColor: Styles().colors.surface, From 841057151853270674f9c780400f3ef070895777 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 24 Sep 2021 10:00:22 +0300 Subject: [PATCH 57/73] Acknowledge vaccinationAppointUrl from app config (#711). --- lib/ui/health/HealthHomePanel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 05fbb404..492116c0 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -1098,7 +1098,7 @@ class _HealthHomePanelState extends State implements Notificati if (Config().vaccinationAppointUrl != null) { if (Connectivity().isNotOffline) { Analytics.instance.logSelect(target: "COVID-19 Make Vaccine Appointment"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: 'https://www.vaccines.gov'))); + Navigator.push(context, CupertinoPageRoute(builder: (context) => WebPanel(url: Config().vaccinationAppointUrl))); } else { AppAlert.showOfflineMessage(context); } From dfb1ee29bdfa5883d5e7807fc2ecd5f77a3cb022 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 24 Sep 2021 10:24:13 +0300 Subject: [PATCH 58/73] Do not evaluate vaccination periods, just display a text saying the the vaccination is not effective yet (#711). --- assets/strings.en.json | 17 ++------------- assets/strings.es.json | 17 ++------------- assets/strings.ja.json | 17 ++------------- assets/strings.zh.json | 17 ++------------- lib/ui/health/HealthHomePanel.dart | 35 +++++------------------------- lib/utils/Utils.dart | 6 ----- 6 files changed, 13 insertions(+), 96 deletions(-) diff --git a/assets/strings.en.json b/assets/strings.en.json index 72d94b35..ffc4a751 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -339,10 +339,8 @@ "panel.covid19home.vaccination.heading.title": "VACCINATION", "panel.covid19home.vaccination.none.title": "Get a vaccine now", "panel.covid19home.vaccination.none.description": "• COVID-19 vaccines are safe\n• COVID-19 vaccines are effective\n• COVID-19 vaccines allow you to safely do more\n• COVID-19 vaccines build safer protection", - "panel.covid19home.vaccination.progress.title": "Get %s vaccination", - "panel.covid19home.vaccination.progress.description": "Get your %s dose of vaccine on %s.", - "panel.covid19home.vaccination.finished.title": "Wait for vaccination to get effective", - "panel.covid19home.vaccination.finished.description": "Your vaccination will become effective after %s.", + "panel.covid19home.vaccination.vaccinated.title": "Vaccinated", + "panel.covid19home.vaccination.vaccinated.description": "Your vaccination is not effective yet.", "panel.covid19home.vaccination.button.appointment.title": "Make an appointment", "panel.covid19home.vaccination.button.appointment.hint": "", @@ -909,17 +907,6 @@ "model.explore.time.at": "at", "model.explore.time.all_day": "All Day", - "model.number.ordinal.long.1": "first", - "model.number.ordinal.long.2": "second", - "model.number.ordinal.long.3": "third", - "model.number.ordinal.long.4": "fourth", - "model.number.ordinal.long.5": "fifth", - "model.number.ordinal.long.6": "sixth", - "model.number.ordinal.long.7": "seventh", - "model.number.ordinal.long.8": "eighth", - "model.number.ordinal.long.9": "ninth", - "model.number.ordinal.long.10": "tenth", - "logic.date_time.greeting.morning": "Good morning", "logic.date_time.greeting.afternoon": "Good afternoon", "logic.date_time.greeting.evening": "Good evening", diff --git a/assets/strings.es.json b/assets/strings.es.json index c9a5fc1a..c9681fb8 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -339,10 +339,8 @@ "panel.covid19home.vaccination.heading.title": "VACUNACIÓN", "panel.covid19home.vaccination.none.title": "Obtenga una vacuna ahora", "panel.covid19home.vaccination.none.description": "• Las vacunas COVID-19 son seguras\n• Las vacunas COVID-19 son efectivas\n• Las vacunas COVID-19 le permiten hacer más de manera segura\n• Las vacunas COVID-19 crean una protección más segura", - "panel.covid19home.vaccination.progress.title": "Recibe la vacuna %s", - "panel.covid19home.vaccination.progress.description": "Obtenga su %s dosis de vacuna el %s.", - "panel.covid19home.vaccination.finished.title": "Espere a que la vacunación sea efectiva", - "panel.covid19home.vaccination.finished.description": "Su vacuna será efectiva después de %s.", + "panel.covid19home.vaccination.vaccinated.title": "Vacunado", + "panel.covid19home.vaccination.vaccinated.description": "Su vacunación aún no es efectiva.", "panel.covid19home.vaccination.button.appointment.title": "Haga una cita", "panel.covid19home.vaccination.button.appointment.hint": "", @@ -905,17 +903,6 @@ "model.explore.time.at": "a", "model.explore.time.all_day": "Todo el dia", - "model.number.ordinal.long.1": "primera", - "model.number.ordinal.long.2": "segunda", - "model.number.ordinal.long.3": "Tercera", - "model.number.ordinal.long.4": "Cuarta", - "model.number.ordinal.long.5": "quinta", - "model.number.ordinal.long.6": "sexta", - "model.number.ordinal.long.7": "séptima", - "model.number.ordinal.long.8": "octava", - "model.number.ordinal.long.9": "novena", - "model.number.ordinal.long.10": "décima", - "logic.date_time.greeting.morning": "Buenos días", "logic.date_time.greeting.afternoon": "Buenas tardes", "logic.date_time.greeting.evening": "Buena noches", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index 378fc60b..acf82069 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -339,10 +339,8 @@ "panel.covid19home.vaccination.heading.title": "ワクチン", "panel.covid19home.vaccination.none.title": "今すぐワクチンを入手", "panel.covid19home.vaccination.none.description": "• COVID-19 疫苗是安全的\n• COVID-19ワクチンは効果的です\n• COVID-19ワクチンはあなたが安全にもっと多くのことをすることを可能にします\n• COVID-19ワクチンはより安全な保護を構築します", - "panel.covid19home.vaccination.progress.title": "%s予防接種を受ける", - "panel.covid19home.vaccination.progress.description": "%sでワクチンの%s投与量を取得します。", - "panel.covid19home.vaccination.finished.title": "予防接種が効果を発揮するのを待つ", - "panel.covid19home.vaccination.finished.description": "予防接種は%s後に有効になります。", + "panel.covid19home.vaccination.vaccinated.title": "ワクチン接種", + "panel.covid19home.vaccination.vaccinated.description": "あなたの予防接種はまだ効果的ではありません。", "panel.covid19home.vaccination.button.appointment.title": "予約する", "panel.covid19home.vaccination.button.appointment.hint": "", @@ -909,17 +907,6 @@ "model.explore.time.at": "at", "model.explore.time.all_day": "一日中", - "model.number.ordinal.long.1": "初め", - "model.number.ordinal.long.2": "2番目", - "model.number.ordinal.long.3": "三番目", - "model.number.ordinal.long.4": "第4", - "model.number.ordinal.long.5": "5番目", - "model.number.ordinal.long.6": "6番目", - "model.number.ordinal.long.7": "7番目", - "model.number.ordinal.long.8": "第8", - "model.number.ordinal.long.9": "9番目", - "model.number.ordinal.long.10": "10番目", - "logic.date_time.greeting.morning": "おはようございます。", "logic.date_time.greeting.afternoon": "こんにちは", "logic.date_time.greeting.evening": "こんばんは", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index eeb9327e..0e4af84c 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -339,10 +339,8 @@ "panel.covid19home.vaccination.heading.title": "疫苗接種", "panel.covid19home.vaccination.none.title": "立即接種疫苗", "panel.covid19home.vaccination.none.description": "• COVID-19 疫苗是安全的\n• COVID-19 疫苗是有效的\n• COVID-19 疫苗可讓您安全地做更多事情\n• COVID-19 疫苗可建立更安全的保護", - "panel.covid19home.vaccination.progress.title": "接種%s疫苗", - "panel.covid19home.vaccination.progress.description": "在%s上接種%s劑量的疫苗。", - "panel.covid19home.vaccination.finished.title": "等待疫苗接種生效", - "panel.covid19home.vaccination.finished.description": "您的疫苗接種將在%s後生效。", + "panel.covid19home.vaccination.vaccinated.title": "已接種", + "panel.covid19home.vaccination.vaccinated.description": "您的疫苗接種尚未生效。", "panel.covid19home.vaccination.button.appointment.title": "預約", "panel.covid19home.vaccination.button.appointment.hint": "", @@ -909,17 +907,6 @@ "model.explore.time.at": "在", "model.explore.time.all_day": "一整天", - "model.number.ordinal.long.1": "第一的", - "model.number.ordinal.long.2": "第二", - "model.number.ordinal.long.3": "第三", - "model.number.ordinal.long.4": "第四", - "model.number.ordinal.long.5": "第五", - "model.number.ordinal.long.6": "第六", - "model.number.ordinal.long.7": "第七", - "model.number.ordinal.long.8": "第八", - "model.number.ordinal.long.9": "第九", - "model.number.ordinal.long.10": "第十", - "logic.date_time.greeting.morning": "早上好", "logic.date_time.greeting.afternoon": "下午好", "logic.date_time.greeting.evening": "晚上好", diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 492116c0..5048288c 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -49,7 +49,6 @@ import 'package:illinois/ui/widgets/RoundedButton.dart'; import 'package:illinois/ui/widgets/SectionTitlePrimary.dart'; import 'package:illinois/ui/widgets/StatusInfoDialog.dart'; import 'package:illinois/utils/Utils.dart'; -import 'package:sprintf/sprintf.dart'; import 'package:url_launcher/url_launcher.dart'; class HealthHomePanel extends StatefulWidget { @@ -685,7 +684,6 @@ class _HealthHomePanelState extends State implements Notificati Widget _buildVaccinationSection() { HealthHistory lastVaccineTaken; - int numberOfTakenVaccines = 0; DateTime nowUtc = DateTime.now().toUtc(); for (HealthHistory historyEntry in Health().history ?? []) { @@ -695,7 +693,6 @@ class _HealthHomePanelState extends State implements Notificati return null; } else if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineTaken?.toLowerCase()) { - numberOfTakenVaccines++; if (lastVaccineTaken == null) { lastVaccineTaken = historyEntry; } @@ -708,9 +705,6 @@ class _HealthHomePanelState extends State implements Notificati String statusTitleText, statusTitleHtml; String statusDescriptionText, statusDescriptionHtml; - //TMP: List vaccinePeriods = AppJson.listIntValue(Config().settings['covid19VaccinePeriods']); - ListvaccinePeriods = [21, 14]; - if (lastVaccineTaken == null) { // No vaccine taken - promote it. statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.none.title', 'Get a vaccine now'); @@ -720,30 +714,11 @@ class _HealthHomePanelState extends State implements Notificati • COVID-19 vaccines allow you to safely do more • COVID-19 vaccines build safer protection"""); } - else if (vaccinePeriods == null) { - // Disable further processing if no periods defined. - return null; - } - else if (numberOfTakenVaccines < vaccinePeriods.length) { - // There is a next vaccine to to take: show when. - headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - - int nextDoseOffset = vaccinePeriods[numberOfTakenVaccines - 1]; - DateTime nextDoseDate = lastVaccineTaken?.dateUtc?.add(Duration(days: nextDoseOffset)); - String nextDoseDateStr = AppDateTime.formatDateTime(nextDoseDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - - String ordinal = AppString.localizedOrdinal(numberOfTakenVaccines + 1) ?? '$numberOfTakenVaccines-th'; - statusTitleText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.progress.title', 'Get %s vaccination'), [ordinal]); - statusDescriptionText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.progress.description', 'Get your %s dose of vaccine on %s.'), [ordinal, nextDoseDateStr]); - } else { - headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - - DateTime vaccineEffectiveDate = lastVaccineTaken?.dateUtc?.add(Duration(days: vaccinePeriods.last)); - String vaccineEffectiveDateStr = AppDateTime.formatDateTime(vaccineEffectiveDate?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; - - statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.finished.title', 'Wait for vaccination to get effective'); - statusDescriptionText = sprintf(Localization().getStringEx('panel.covid19home.vaccination.finished.description', 'Your vaccination will become effective after %s.'), [vaccineEffectiveDateStr]); + // Vaccinated, but not effective yet. + headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode); + statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.vaccinated.title', 'Vaccinated'); + statusDescriptionText = Localization().getStringEx('panel.covid19home.vaccination.vaccinated.description', 'Your vaccination is not effective yet.'); } List contentWidgets = [ @@ -810,7 +785,7 @@ class _HealthHomePanelState extends State implements Notificati ],), ]; - if ((numberOfTakenVaccines < vaccinePeriods.length) && (Config().vaccinationAppointUrl != null)) { + if (Config().vaccinationAppointUrl != null) { contentList.addAll([ Container(margin: EdgeInsets.only(top: 14, bottom: 14), height: 1, color: Styles().colors.fillColorPrimaryTransparent015,), diff --git a/lib/utils/Utils.dart b/lib/utils/Utils.dart index db9dfc85..3478edd4 100644 --- a/lib/utils/Utils.dart +++ b/lib/utils/Utils.dart @@ -109,12 +109,6 @@ class AppString { } return null; } - - static List _defaultLongOrdinals = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth']; - - static String localizedOrdinal(int value) { - return ((1 <= value) && (value <= _defaultLongOrdinals.length)) ? Localization().getStringEx('model.number.ordinal.long.$value', _defaultLongOrdinals[value - 1]) : null; - } } class AppCollection { From c5d611114d300d55dc73f2b33fcfe560f4daa1f2 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 24 Sep 2021 10:30:38 +0300 Subject: [PATCH 59/73] Updated CHANGELOG.md (#711). --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf05f64..d6085cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Changed +- Updated vaccination widget content and strings [#711](https://github.com/rokwire/safer-illinois-app/issues/711). ## [2.11.1] - 2021-09-23 ### Changed From 36e621c371a141d5bf6b61d78bd50a79e66d75c2 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 24 Sep 2021 16:11:20 +0300 Subject: [PATCH 60/73] version: 2.11.2+1102 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6085cf1..01cd8e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.11.2] - 2021-09-24 ### Changed - Updated vaccination widget content and strings [#711](https://github.com/rokwire/safer-illinois-app/issues/711). diff --git a/pubspec.yaml b/pubspec.yaml index eb74607e..cd016122 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.11.1+1101 +version: 2.11.2+1102 environment: sdk: ">=2.2.0 <3.0.0" From 789059d074ef1c489a48236592c5dcf13268b4c2 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Mon, 27 Sep 2021 11:05:02 +0300 Subject: [PATCH 61/73] Acknowledge isVaccineTaken/Effective getters from HealthHistoryBlob to determine vaccination status. --- lib/ui/health/HealthHomePanel.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 5048288c..9126e8ae 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -687,12 +687,12 @@ class _HealthHomePanelState extends State implements Notificati DateTime nowUtc = DateTime.now().toUtc(); for (HealthHistory historyEntry in Health().history ?? []) { - if ((historyEntry.dateUtc != null) && historyEntry.dateUtc.isBefore(nowUtc) && historyEntry.isVaccine) { - if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineEffective?.toLowerCase()) { + if ((historyEntry.dateUtc != null) && historyEntry.dateUtc.isBefore(nowUtc) && historyEntry.isVaccine && (historyEntry.blob != null)) { + if (historyEntry.blob.isVaccineEffective) { // 5.2.4 When effective then hide the widget return null; } - else if (historyEntry.blob?.vaccine?.toLowerCase() == HealthHistoryBlob.VaccineTaken?.toLowerCase()) { + else if (historyEntry.blob.isVaccineTaken) { if (lastVaccineTaken == null) { lastVaccineTaken = historyEntry; } From a9d78907d2023ea85bab5989858a8ab347886bdb Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 10:13:39 +0300 Subject: [PATCH 62/73] Removed vaccine taken event handling, process middle state of vaccination widget based on current date time and vaccine effective date time in a future (#715). --- assets/strings.en.json | 3 -- assets/strings.es.json | 3 -- assets/strings.ja.json | 3 -- assets/strings.zh.json | 3 -- lib/model/Health.dart | 5 ---- lib/service/Health.dart | 3 +- lib/ui/debug/DebugCreateEventPanel.dart | 30 ++----------------- lib/ui/health/HealthHistoryPanel.dart | 5 +--- lib/ui/health/HealthHomePanel.dart | 35 +++++++--------------- lib/ui/health/HealthStatusUpdatePanel.dart | 3 -- 10 files changed, 15 insertions(+), 78 deletions(-) diff --git a/assets/strings.en.json b/assets/strings.en.json index ffc4a751..4ffe7914 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -325,7 +325,6 @@ "panel.covid19home.label.contact_trace.title": "Contact Trace", "panel.covid19home.label.reported_symptoms.title": "Self Reported Symptoms", "panel.covid19home.label.vaccine.effective.title": "Vaccine Effective", - "panel.covid19home.label.vaccine.taken.title": "Vaccine Taken", "panel.covid19home.label.vaccine.title": "Vaccine", "panel.covid19home.button.info.title": "Info ", "panel.covid19home.label.access.granted": "Building access granted", @@ -617,7 +616,6 @@ "panel.health.covid19.history.label.contact_trace.details": "contact trace: ", "panel.health.covid19.history.label.vaccine.effective.title": "Vaccine Effective", "panel.health.covid19.history.label.vaccine.effective.details": "vaccine effective: ", - "panel.health.covid19.history.label.vaccine.taken.title": "Vaccine Taken", "panel.health.covid19.history.label.vaccine.taken.details": "vaccine taken: ", "panel.health.covid19.history.label.vaccine.title": "Vaccine", "panel.health.covid19.history.label.vaccine.details": "vaccine: ", @@ -715,7 +713,6 @@ "panel.health.status_update.label.reason.exposed.title": "You were exposed to someone who was likely infected", "panel.health.status_update.label.reason.exposure.detail": "Duration of exposure: ", "panel.health.status_update.label.reason.vaccine.effective.title": "Your COVID-19 vaccination has been verified.", - "panel.health.status_update.label.reason.vaccine.taken.title": "Your vaccine is taken.", "panel.health.status_update.label.reason.vaccine.title": "Your vaccine is applied.", "panel.health.status_update.label.reason.action.title": "Health authorities require you to take an action.", "panel.health.status_update.label.reason.action.detail": "Action Required: ", diff --git a/assets/strings.es.json b/assets/strings.es.json index c9681fb8..84578310 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -325,7 +325,6 @@ "panel.covid19home.label.contact_trace.title": "Seguimiento de contacto", "panel.covid19home.label.reported_symptoms.title": "Síntomas autoinformados", "panel.covid19home.label.vaccine.effective.title": "Vacuna Eficaz", - "panel.covid19home.label.vaccine.taken.title": "Vacuna Tomada", "panel.covid19home.label.vaccine.title": "Vacuna", "panel.covid19home.button.info.title": "Información ", "panel.covid19home.label.access.granted": "Acceso al edificio concedido", @@ -617,7 +616,6 @@ "panel.health.covid19.history.label.contact_trace.details": "seguimiento de contacto: ", "panel.health.covid19.history.label.vaccine.effective.title": "Vacuna Eficaz", "panel.health.covid19.history.label.vaccine.effective.details": "vacuna eficaz: ", - "panel.health.covid19.history.label.vaccine.taken.title": "Vacuna Tomada", "panel.health.covid19.history.label.vaccine.taken.details": "vacuna tomada: ", "panel.health.covid19.history.label.vaccine.title": "Vacuna", "panel.health.covid19.history.label.vaccine.details": "vacuna: ", @@ -715,7 +713,6 @@ "panel.health.status_update.label.reason.exposed.title": "Estuvo expuesto a alguien que probablemente estaba infectado", "panel.health.status_update.label.reason.exposure.detail": "Duración de exposición:", "panel.health.status_update.label.reason.vaccine.effective.title": "Se ha verificado su vacuna COVID-19.", - "panel.health.status_update.label.reason.vaccine.taken.title": "Se toma su vacuna.", "panel.health.status_update.label.reason.vaccine.title": "Se aplica su vacuna.", "panel.health.status_update.label.reason.action.title": "Las autoridades sanitarias le exigen que actúe.", "panel.health.status_update.label.reason.action.detail": "Acción requerida:", diff --git a/assets/strings.ja.json b/assets/strings.ja.json index acf82069..2b28685b 100644 --- a/assets/strings.ja.json +++ b/assets/strings.ja.json @@ -325,7 +325,6 @@ "panel.covid19home.label.contact_trace.title": "Contact Trace", "panel.covid19home.label.reported_symptoms.title": "Self Reported Symptoms", "panel.covid19home.label.vaccine.effective.title": "Vaccine Effective", - "panel.covid19home.label.vaccine.taken.title": "Vaccine Taken", "panel.covid19home.label.vaccine.title": "Vaccine", "panel.covid19home.button.info.title": "情報 ", "panel.covid19home.label.access.granted": "建物への出入りが承認されました", @@ -617,7 +616,6 @@ "panel.health.covid19.history.label.contact_trace.details": "contact trace: ", "panel.health.covid19.history.label.vaccine.effective.title": "Vaccine Effective", "panel.health.covid19.history.label.vaccine.effective.details": "vaccine effective: ", - "panel.health.covid19.history.label.vaccine.taken.title": "Vaccine Taken", "panel.health.covid19.history.label.vaccine.taken.details": "vaccine taken: ", "panel.health.covid19.history.label.vaccine.title": "Vaccine", "panel.health.covid19.history.label.vaccine.details": "vaccine: ", @@ -715,7 +713,6 @@ "panel.health.status_update.label.reason.exposed.title": "You were exposed to someone who was likely infected", "panel.health.status_update.label.reason.exposure.detail": "Duration of exposure: ", "panel.health.status_update.label.reason.vaccine.effective.title": "Your COVID-19 vaccination has been verified.", - "panel.health.status_update.label.reason.vaccine.taken.title": "Your vaccine is taken.", "panel.health.status_update.label.reason.vaccine.title": "Your vaccine is applied.", "panel.health.status_update.label.reason.action.title": "Health authorities require you to take an action.", "panel.health.status_update.label.reason.action.detail": "Action Required: ", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index 0e4af84c..2c74cf7e 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -325,7 +325,6 @@ "panel.covid19home.label.contact_trace.title": "接触痕迹", "panel.covid19home.label.reported_symptoms.title": "自述症状", "panel.covid19home.label.vaccine.effective.title": "疫苗有效", - "panel.covid19home.label.vaccine.taken.title": "接種疫苗", "panel.covid19home.label.vaccine.title": "疫苗", "panel.covid19home.button.info.title": "信息 ", "panel.covid19home.label.access.granted": "授予建築物訪問權限", @@ -617,7 +616,6 @@ "panel.health.covid19.history.label.contact_trace.details": "接触者追踪: ", "panel.health.covid19.history.label.vaccine.effective.title": "疫苗有效", "panel.health.covid19.history.label.vaccine.effective.details": "疫苗有效: ", - "panel.health.covid19.history.label.vaccine.taken.title": "接種疫苗", "panel.health.covid19.history.label.vaccine.taken.details": "接種疫苗: ", "panel.health.covid19.history.label.vaccine.title": "疫苗", "panel.health.covid19.history.label.vaccine.details": "疫苗: ", @@ -715,7 +713,6 @@ "panel.health.status_update.label.reason.exposed.title": "你接触过可能被感染的人", "panel.health.status_update.label.reason.exposure.detail": "暴露时间: ", "panel.health.status_update.label.reason.vaccine.effective.title": "您的 COVID-19 疫苗接種已通過驗證。", - "panel.health.status_update.label.reason.vaccine.taken.title": "您的疫苗已經服用。", "panel.health.status_update.label.reason.vaccine.title": "您的疫苗已接種。", "panel.health.status_update.label.reason.action.title": "衛生當局要求您採取行動。", "panel.health.status_update.label.reason.action.detail": "待办行动: ", diff --git a/lib/model/Health.dart b/lib/model/Health.dart index 409d6627..16349a49 100644 --- a/lib/model/Health.dart +++ b/lib/model/Health.dart @@ -732,7 +732,6 @@ class HealthHistoryBlob { final List extras; static const String VaccineEffective = "Effective"; - static const String VaccineTaken = "Taken"; HealthHistoryBlob({ this.provider, this.providerId, this.location, this.locationId, this.countyId, this.testType, this.testResult, @@ -864,10 +863,6 @@ class HealthHistoryBlob { return (vaccine != null) && (vaccine.toLowerCase() == VaccineEffective.toLowerCase()); } - bool get isVaccineTaken { - return (vaccine != null) && (vaccine.toLowerCase() == VaccineTaken.toLowerCase()); - } - bool get isAction { return (actionType != null) || (actionTitle != null) || (actionText != null) || (actionParams != null); } diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 5fb1aa2b..75cea585 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -1640,7 +1640,8 @@ class Health with Service implements NotificationsListener { // Vaccination bool get isVaccinated { - return (HealthHistory.mostRecentVaccine(_history, vaccine: HealthHistoryBlob.VaccineEffective) != null); + HealthHistory vaccine = HealthHistory.mostRecentVaccine(Health().history); + return (vaccine.blob != null) && (vaccine?.blob?.isVaccineEffective ?? false) && (vaccine.dateUtc != null) && vaccine.dateUtc.isBefore(DateTime.now().toUtc()); } // Current Server Time diff --git a/lib/ui/debug/DebugCreateEventPanel.dart b/lib/ui/debug/DebugCreateEventPanel.dart index 2035c0e5..13c2c7b3 100644 --- a/lib/ui/debug/DebugCreateEventPanel.dart +++ b/lib/ui/debug/DebugCreateEventPanel.dart @@ -295,7 +295,7 @@ class _DebugCreateEventPanelState extends State { Row(children: [ Expanded(child: - RoundedButton(label: "Vacc Effective", + RoundedButton(label: "Vaccine Effective", textColor: Styles().colors.fillColorPrimary, borderColor: Styles().colors.fillColorSecondary, backgroundColor: Styles().colors.white, @@ -306,14 +306,7 @@ class _DebugCreateEventPanelState extends State { ), Container(width: 4,), Expanded(child: - RoundedButton(label: "Vacc Taken", - textColor: Styles().colors.fillColorPrimary, - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - fontFamily: Styles().fontFamilies.bold, - fontSize: 16, borderWidth: 2, height: 42, - onTap:() { _onPopulate(this._sampleVacineTakenBlob); } - ), + Container() ), ],), @@ -546,25 +539,6 @@ class _DebugCreateEventPanelState extends State { ] }''';} - String get _sampleVacineTakenBlob { - DateTime nowLocal = DateTime.now(); - String dateString = healthDateTimeToString(nowLocal.toUtc()); - - String dose2String = DateFormat("MMMM d, yyyy HH:mm").format(nowLocal); - - DateTime dose1Local = nowLocal.subtract(Duration(days: 21)); - String dose1String = DateFormat("MMMM d, yyyy HH:mm").format(dose1Local); - - return '''{ - "Date": "$dateString", - "Vaccine": "${HealthHistoryBlob.VaccineTaken}", - "Extra": [ - {"display_name": "Vaccine", "display_value": "Sputnik V"}, - {"display_name": "2nd Dose", "display_value": "$dose2String"}, - {"display_name": "1st Dose", "display_value": "$dose1String"} - ] -}''';} - String get _sampleActionQuarantineOnBlob { DateTime nowLocal = DateTime.now(); String dateString = healthDateTimeToString(nowLocal.toUtc()); diff --git a/lib/ui/health/HealthHistoryPanel.dart b/lib/ui/health/HealthHistoryPanel.dart index ac745a14..e050bf7e 100644 --- a/lib/ui/health/HealthHistoryPanel.dart +++ b/lib/ui/health/HealthHistoryPanel.dart @@ -530,11 +530,8 @@ class _HealthHistoryEntryState extends State<_HealthHistoryEntry> with SingleTic if (widget.historyEntry?.blob?.isVaccineEffective ?? false) { title = Localization().getStringEx("panel.health.covid19.history.label.vaccine.effective.title", "Vaccine Effective"); } - else if (widget.historyEntry?.blob?.isVaccineTaken ?? false) { - title = Localization().getStringEx("panel.health.covid19.history.label.vaccine.taken.title", "Vaccine Taken"); - } else { - title = Localization().getStringEx("panel.health.covid19.history.label.vaccine.title", "Vaccine Taken"); + title = Localization().getStringEx("panel.health.covid19.history.label.vaccine.title", "Vaccine"); } String providerTitle = widget.historyEntry?.blob?.provider ?? Localization().getStringEx("app.common.label.other", "Other"); details.addAll([ diff --git a/lib/ui/health/HealthHomePanel.dart b/lib/ui/health/HealthHomePanel.dart index 9126e8ae..aa8717e9 100644 --- a/lib/ui/health/HealthHomePanel.dart +++ b/lib/ui/health/HealthHomePanel.dart @@ -374,9 +374,6 @@ class _HealthHomePanelState extends State implements Notificati if (blob.isVaccineEffective) { historyTitle = Localization().getStringEx("panel.covid19home.label.vaccine.effective.title", "Vaccine Effective"); } - else if (blob.isVaccineTaken) { - historyTitle = Localization().getStringEx("panel.covid19home.label.vaccine.taken.title", "Vaccine Taken"); - } else { historyTitle = Localization().getStringEx("panel.covid19home.label.vaccine.title", "Vaccine"); } @@ -683,30 +680,14 @@ class _HealthHomePanelState extends State implements Notificati Widget _buildVaccinationSection() { - HealthHistory lastVaccineTaken; - DateTime nowUtc = DateTime.now().toUtc(); - - for (HealthHistory historyEntry in Health().history ?? []) { - if ((historyEntry.dateUtc != null) && historyEntry.dateUtc.isBefore(nowUtc) && historyEntry.isVaccine && (historyEntry.blob != null)) { - if (historyEntry.blob.isVaccineEffective) { - // 5.2.4 When effective then hide the widget - return null; - } - else if (historyEntry.blob.isVaccineTaken) { - if (lastVaccineTaken == null) { - lastVaccineTaken = historyEntry; - } - } - } - } - - String headingTitle = Localization().getStringEx('panel.covid19home.vaccination.heading.title', 'VACCINATION'); String headingDate; String statusTitleText, statusTitleHtml; String statusDescriptionText, statusDescriptionHtml; + String headingTitle = Localization().getStringEx('panel.covid19home.vaccination.heading.title', 'VACCINATION'); - if (lastVaccineTaken == null) { - // No vaccine taken - promote it. + HealthHistory vaccine = HealthHistory.mostRecentVaccine(Health().history); + if (vaccine == null) { + // No vaccine at all - promote it. statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.none.title', 'Get a vaccine now'); statusDescriptionText = Localization().getStringEx('panel.covid19home.vaccination.none.description', """ • COVID-19 vaccines are safe @@ -714,9 +695,13 @@ class _HealthHomePanelState extends State implements Notificati • COVID-19 vaccines allow you to safely do more • COVID-19 vaccines build safer protection"""); } + else if ((vaccine.blob != null) && (vaccine.blob.isVaccineEffective) && (vaccine.dateUtc != null) && vaccine.dateUtc.isBefore(DateTime.now().toUtc())) { + // 5.2.4 When effective then hide the widget + return null; + } else { // Vaccinated, but not effective yet. - headingDate = AppDateTime.formatDateTime(lastVaccineTaken?.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode); + headingDate = AppDateTime.formatDateTime(vaccine.dateUtc?.toLocal(), format:"MMMM dd, yyyy", locale: Localization().currentLocale?.languageCode) ?? ''; statusTitleText = Localization().getStringEx('panel.covid19home.vaccination.vaccinated.title', 'Vaccinated'); statusDescriptionText = Localization().getStringEx('panel.covid19home.vaccination.vaccinated.description', 'Your vaccination is not effective yet.'); } @@ -785,7 +770,7 @@ class _HealthHomePanelState extends State implements Notificati ],), ]; - if (Config().vaccinationAppointUrl != null) { + if ((vaccine == null) && (Config().vaccinationAppointUrl != null)) { contentList.addAll([ Container(margin: EdgeInsets.only(top: 14, bottom: 14), height: 1, color: Styles().colors.fillColorPrimaryTransparent015,), diff --git a/lib/ui/health/HealthStatusUpdatePanel.dart b/lib/ui/health/HealthStatusUpdatePanel.dart index 5b69a825..d1d1c97a 100644 --- a/lib/ui/health/HealthStatusUpdatePanel.dart +++ b/lib/ui/health/HealthStatusUpdatePanel.dart @@ -221,9 +221,6 @@ class _HealthStatusUpdatePanelState extends State { if (history.isVaccineEffective) { noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.effective.title", "Your COVID-19 vaccination has been verified."); } - else if (history.isVaccineTaken) { - noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.taken.title", "Your vaccine is taken."); - } else { noticeText = Localization().getStringEx("panel.health.status_update.label.reason.vaccine.title", "Your vaccine is applied."); } From fae7ee26a7b85833224bbaa0f75e74a679ce5d2a Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 10:14:49 +0300 Subject: [PATCH 63/73] Show "Delete History" button when display content in History panel is empty. --- lib/ui/health/HealthHistoryPanel.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ui/health/HealthHistoryPanel.dart b/lib/ui/health/HealthHistoryPanel.dart index e050bf7e..053561a7 100644 --- a/lib/ui/health/HealthHistoryPanel.dart +++ b/lib/ui/health/HealthHistoryPanel.dart @@ -165,6 +165,7 @@ class _HealthHistoryPanelState extends State implements Noti style: TextStyle(fontSize: 16, fontFamily: Styles().fontFamilies.regular, color: Styles().colors.textSurface)), Expanded(child: Container(),), _buildRepostButton(), + Visibility(visible: !kReleaseMode || Organizations().isDevEnvironment, child: _buildRemoveMyInfoButton()), Container(height: 10,), ], ))); From 0206b618a342f2eed6632cdd4ebc5fdea8953648 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 10:16:45 +0300 Subject: [PATCH 64/73] Updated CHANGELOG.md (#715) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01cd8e3b..4040efa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +### Deleted +- Removed vaccine taken event handling [#715](https://github.com/rokwire/safer-illinois-app/issues/715). ## [2.11.2] - 2021-09-24 ### Changed From 7c2e6ed983111c8ae0c58bd47c037f95596e6ed3 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 10:44:07 +0300 Subject: [PATCH 65/73] notifyHistoryUpdated when clearing history. --- lib/service/Health.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/service/Health.dart b/lib/service/Health.dart index 75cea585..317b5812 100644 --- a/lib/service/Health.dart +++ b/lib/service/Health.dart @@ -936,8 +936,12 @@ class Health with Service implements NotificationsListener { } Future clearHistory() async { + List history = _history; if (await _clearNetHistory()) { - await _rebuildStatus(); + if (!ListEquality().equals(history, _history)) { + _notify(notifyHistoryUpdated); + await _rebuildStatus(); + } return true; } return false; From 6d3dadb036054fa6c300e4a832d373d46aae38e7 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 10:44:27 +0300 Subject: [PATCH 66/73] Added clear history button in Debug Home panel. --- lib/ui/debug/DebugHomePanel.dart | 117 +++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/lib/ui/debug/DebugHomePanel.dart b/lib/ui/debug/DebugHomePanel.dart index c929dcab..30b5d778 100644 --- a/lib/ui/debug/DebugHomePanel.dart +++ b/lib/ui/debug/DebugHomePanel.dart @@ -23,6 +23,7 @@ import 'package:flutter/services.dart'; import 'package:illinois/model/Organization.dart'; import 'package:illinois/service/Auth.dart'; import 'package:illinois/service/FirebaseMessaging.dart'; +import 'package:illinois/service/Health.dart'; import 'package:illinois/service/Localization.dart'; import 'package:illinois/service/NotificationService.dart'; import 'package:illinois/service/Organizations.dart'; @@ -57,6 +58,7 @@ class _DebugHomePanelState extends State implements Notification String _environment; bool _switchingEnvironment; + bool _removingHistory; @override void initState() { @@ -218,13 +220,25 @@ class _DebugHomePanelState extends State implements Notification onTap: _onTapCreateEvent)), Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5), - child: RoundedButton( - label: "COVID-19 Create Exposure", + child: Stack(children: [ + RoundedButton( + label: "COVID-19 Clear History", backgroundColor: Styles().colors.background, fontSize: 16.0, textColor: Styles().colors.fillColorPrimary, borderColor: Styles().colors.fillColorPrimary, - onTap: _onTapTraceCovid19Exposure)), + onTap: _onTapClearHistory), + Visibility(visible: _removingHistory == true, child: + Padding(padding: EdgeInsets.symmetric(vertical: 12), child: + Center(child: + Container(width: 24, height: 24, child: + CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorPrimary)), + ), + ), + ), + ), + + ],),), Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5), child: RoundedButton( @@ -234,6 +248,15 @@ class _DebugHomePanelState extends State implements Notification textColor: Styles().colors.fillColorPrimary, borderColor: Styles().colors.fillColorPrimary, onTap: _onTapReportCovid19Symptoms)), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5), + child: RoundedButton( + label: "COVID-19 Create Exposure", + backgroundColor: Styles().colors.background, + fontSize: 16.0, + textColor: Styles().colors.fillColorPrimary, + borderColor: Styles().colors.fillColorPrimary, + onTap: _onTapTraceCovid19Exposure)), Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5), child: RoundedButton( @@ -381,14 +404,20 @@ class _DebugHomePanelState extends State implements Notification Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugCreateEventPanel())); } - void _onTapTraceCovid19Exposure() { - Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugContactTraceReportPanel())); + void _onTapClearHistory() { + if (_removingHistory != true) { + showDialog(context: context, builder: (context) => _buildRemoveHistoryDialog(context)); + } } void _onTapReportCovid19Symptoms() { Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugSymptomsReportPanel())); } + void _onTapTraceCovid19Exposure() { + Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugContactTraceReportPanel())); + } + void _onTapCovid19Exposures() { Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugExposurePanel())); } @@ -607,4 +636,82 @@ class _DebugHomePanelState extends State implements Notification }); }); } + + + ////////////////////////// + // Delete History + + Widget _buildRemoveHistoryDialog(BuildContext context) { + return StatefulBuilder(builder: (context, setState) { + return ClipRRect(borderRadius: BorderRadius.all(Radius.circular(8)), child: + Dialog(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8),), child: + Column(mainAxisSize: MainAxisSize.min, children: [ + Row(children: [ + Expanded(child: + Container(decoration: BoxDecoration(color: Styles().colors.fillColorPrimary, borderRadius: BorderRadius.vertical(top: Radius.circular(8)),), child: + Padding(padding: EdgeInsets.all(8), child: + Row(children: [ + Expanded(child: + Center(child: + Text("Clear COVID-19 event history?", style: TextStyle(fontSize: 20, color: Colors.white),), + ), + ), + GestureDetector(onTap: () => Navigator.pop(context), child: + Container(height: 30, width: 30, decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(15)), border: Border.all(color: Styles().colors.white, width: 2), ), child: + Center(child: + Text('\u00D7', style: TextStyle(fontSize: 24, color: Colors.white, ), ), + ), + ), + ), + ],), + ), + ), + ), + ],), + Container(height: 26,), + Padding(padding: const EdgeInsets.symmetric(horizontal: 18), child: + Text("This will permanently remove all COVID-19 event history.", textAlign: TextAlign.left, style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black),), + ), + Container(height: 26,), + Text("Are you sure?", textAlign: TextAlign.center, style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 16, color: Colors.black), ), + Container(height: 16,), + Padding(padding: const EdgeInsets.all(8.0), child: + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Expanded(child: + RoundedButton( + onTap: () { Navigator.pop(context); }, + backgroundColor: Colors.transparent, + borderColor: Styles().colors.fillColorPrimary, + textColor: Styles().colors.fillColorPrimary, + label: 'No'), + ), + Container(width: 10,), + Expanded(child: + RoundedButton( + onTap: () => _onClearHistory(), + backgroundColor: Styles().colors.fillColorSecondaryVariant, + borderColor: Styles().colors.fillColorSecondaryVariant, + textColor: Styles().colors.surface, + label: 'Yes', + height: 48,), + ), + ],), + ), + ],), + ), + ); + },); + } + + void _onClearHistory() { + Navigator.pop(context); + + if (_removingHistory != true) { + setState(() { _removingHistory = true; }); + Health().clearHistory().then((bool result) { + setState(() {_removingHistory = false;}); + AppAlert.showDialogResult(context, (result == true) ? 'COVID-19 event history successfully cleared.' : 'Failed to clear COVID-19 event history.'); + }); + } + } } From a4ff1060ea89d073c2e3e11ac2ad6ffea770e188 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 11:18:48 +0300 Subject: [PATCH 67/73] Removed Settings2 section (unused). --- lib/ui/settings2/Settings2ConsentPanel.dart | 245 ----- .../Settings2ExposureNotificationsPanel.dart | 248 ----- .../settings2/Settings2GovernmentIdPanel.dart | 694 -------------- lib/ui/settings2/Settings2HomePanel.dart | 845 ------------------ .../Settings2TransferEncryptionKeyPanel.dart | 330 ------- 5 files changed, 2362 deletions(-) delete mode 100644 lib/ui/settings2/Settings2ConsentPanel.dart delete mode 100644 lib/ui/settings2/Settings2ExposureNotificationsPanel.dart delete mode 100644 lib/ui/settings2/Settings2GovernmentIdPanel.dart delete mode 100644 lib/ui/settings2/Settings2HomePanel.dart delete mode 100644 lib/ui/settings2/Settings2TransferEncryptionKeyPanel.dart diff --git a/lib/ui/settings2/Settings2ConsentPanel.dart b/lib/ui/settings2/Settings2ConsentPanel.dart deleted file mode 100644 index 401f695c..00000000 --- a/lib/ui/settings2/Settings2ConsentPanel.dart +++ /dev/null @@ -1,245 +0,0 @@ - - -import 'package:flutter/material.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Health.dart'; -import 'package:illinois/service/NotificationService.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/ui/widgets/HeaderBar.dart'; -import 'package:illinois/ui/widgets/RibbonButton.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; - -class Settings2ConsentPanel extends StatefulWidget{ - Settings2ConsentPanel(); - _Settings2ConsentPanelState createState() => _Settings2ConsentPanelState(); -} - -class _Settings2ConsentPanelState extends State implements NotificationsListener{ - - bool _isDisabling = false; - bool _isEnabling = false; - - @override - void initState() { - super.initState(); - NotificationService().subscribe(this, [Health.notifyUserUpdated]); - } - - @override - void dispose() { - super.dispose(); - NotificationService().unsubscribe(this); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: SimpleHeaderBarWithBack( - context: context, - titleWidget: Text( - 'Automatic Test Results', - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontFamily: Styles().fontFamilies.extraBold, - letterSpacing: 1.0, - ), - textAlign: TextAlign.center, - ), - ), - body: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(22), - child: Column( - children: [ - Text('This feature allows you to receive COVID-19 test results from your healthcare provider directly in the app. Results are encrypted, so only you can see them.', - style: TextStyle( - color: Styles().colors.fillColorPrimary, - fontFamily: Styles().fontFamilies.regular, - fontSize: 16, - ), - ), - Container(height: 18,), - Stack( - alignment: Alignment.center, - children: [ - ToggleRibbonButton( - label: "I consent to connect test results from my healthcare provider with the Safer Illinois app.", - style: TextStyle( - color: Styles().colors.fillColorPrimary, - fontFamily: Styles().fontFamilies.medium, - fontSize: 14 - ), - height: null, - border: Border.all(width: 1, color: Styles().colors.surfaceAccent), - borderRadius: BorderRadius.all(Radius.circular(4)), - toggled: Health().user.consentTestResults, - onTap: (){ - if(!Health().user.consentTestResults){ - _onConsentEnabled(); - } - else{ - showDialog(context: context, builder: (context) => _buildConsentDialog(context)); - } - }, - ), - _isEnabling ? Align(alignment: Alignment.center, child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorPrimary), strokeWidth: 2,)) : Container() - ], - ), - ], - ), - ), - ), - ); - } - - Widget _buildConsentDialog(BuildContext context) { - return StatefulBuilder( - builder: (context, setState){ - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8.0)) - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: Styles().colors.fillColorPrimary, - borderRadius: BorderRadius.vertical(top: Radius.circular(8)) - ), - child: Padding( - padding: EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: Center( - child: Text( - "Automatic Test Results", - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 24, color: Colors.white), - ), - ), - ), - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all(color: Styles().colors.white, width: 2), - ), - child: Center( - child: Text( - '\u00D7', - style: TextStyle( - fontSize: 24, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: Text( - "By removing your consent, you will no longer receive automatic test results from your health provider.\n\nPrevious test results will remain in your COVID-19 event history. You can delete them by accessing Your COVID-19 Event History in the Privacy Center.", - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: RoundedButton( - onTap: () { - Analytics.instance.logAlert(text: "Consent", selection: "No"); - Navigator.pop(context); - }, - backgroundColor: Colors.transparent, - borderColor: Styles().colors.fillColorPrimary, - textColor: Styles().colors.fillColorPrimary, - label: "No"), - ), - Container( - width: 10, - ), - Expanded( - child: Stack( - alignment: Alignment.center, - children: [ - RoundedButton( - onTap: () => _onConsentDisabled(context, setState), - backgroundColor: Styles().colors.fillColorSecondaryVariant, - borderColor: Styles().colors.fillColorSecondaryVariant, - textColor: Styles().colors.surface, - label: 'Remove Consent'), - _isDisabling ? Align(alignment: Alignment.center, child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorPrimary), strokeWidth: 2,)) : Container() - ], - ), - ), - ], - ), - ), - ], - ), - ); - }, - ); - } - - void _onConsentDisabled(BuildContext context, StateSetter _setState){ - if(_isDisabling){ - return; - } - _setState((){ - _isDisabling = true; - }); - Health().loginUser(consentTestResults: false).whenComplete((){ - _setState((){ - _isDisabling = false; - }); - Navigator.pop(context); - }); - } - - void _onConsentEnabled(){ - if(_isEnabling){ - return; - } - setState((){ - _isEnabling = true; - }); - Health().loginUser(consentTestResults: true).whenComplete((){ - setState((){ - _isEnabling = false; - }); - }); - } - - @override - void onNotification(String name, param) { - if(name == Health.notifyUserUpdated){ - setState(() {}); - } - } -} \ No newline at end of file diff --git a/lib/ui/settings2/Settings2ExposureNotificationsPanel.dart b/lib/ui/settings2/Settings2ExposureNotificationsPanel.dart deleted file mode 100644 index cc573de8..00000000 --- a/lib/ui/settings2/Settings2ExposureNotificationsPanel.dart +++ /dev/null @@ -1,248 +0,0 @@ - - -import 'package:flutter/material.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Health.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/NotificationService.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/ui/widgets/HeaderBar.dart'; -import 'package:illinois/ui/widgets/RibbonButton.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; - -class Settings2ExposureNotificationsPanel extends StatefulWidget{ - Settings2ExposureNotificationsPanel(); - _Settings2ExposureNotificationsPanelState createState() => _Settings2ExposureNotificationsPanelState(); -} - -class _Settings2ExposureNotificationsPanelState extends State implements NotificationsListener{ - - bool _isDisabling = false; - bool _isEnabling = false; - - @override - void initState() { - super.initState(); - NotificationService().subscribe(this, [Health.notifyUserUpdated]); - } - - @override - void dispose() { - super.dispose(); - NotificationService().unsubscribe(this); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: SimpleHeaderBarWithBack( - context: context, - titleWidget: Text( - 'Exposure Notificaitons', - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontFamily: Styles().fontFamilies.extraBold, - letterSpacing: 1.0, - ), - textAlign: TextAlign.center, - ), - ), - body: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(22), - child: Column( - children: [ - Text('If you opt in to exposure notifications, you allow your phone to send an anonymous Bluetooth signal to nearby Safer Illinois app users who are also using this feature. Your phone will receive and record a signal from their phones as well. If one of those users tests positive for COVID-19 in the next 14 days, the app will alert you to your potential exposure and advise you on next steps.\n\nYour identity and health status will remain anonymous, as will the identity and health status of all other users.', - style: TextStyle( - color: Styles().colors.fillColorPrimary, - fontFamily: Styles().fontFamilies.regular, - fontSize: 16, - ), - ), - Container(height: 18,), - Stack( - alignment: Alignment.center, - children: [ - ToggleRibbonButton( - label: "I opt in to participate in the Exposure Notification System (requires Bluetooth to be ON)", - style: TextStyle( - color: Styles().colors.fillColorPrimary, - fontFamily: Styles().fontFamilies.medium, - fontSize: 14 - ), - height: null, - border: Border.all(width: 1, color: Styles().colors.surfaceAccent), - borderRadius: BorderRadius.all(Radius.circular(4)), - toggled: Health().user.consentExposureNotification, - onTap: (){ - if(!Health().user.consentExposureNotification){ - _onConsentEnabled(); - } - else{ - showDialog(context: context, builder: (context) => _buildConsentDialog(context)); - } - }, - ), - _isEnabling ? Align(alignment: Alignment.center, child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorPrimary), strokeWidth: 2,)) : Container() - ], - ), - ], - ), - ), - ), - ); - } - - Widget _buildConsentDialog(BuildContext context) { - return StatefulBuilder( - builder: (context, setState){ - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8.0)) - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: Styles().colors.fillColorPrimary, - borderRadius: BorderRadius.vertical(top: Radius.circular(8)) - ), - child: Padding( - padding: EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: Center( - child: Text( - "Exposure Notifications", - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 24, color: Colors.white), - ), - ), - ), - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all(color: Styles().colors.white, width: 2), - ), - child: Center( - child: Text( - '\u00D7', - style: TextStyle( - fontSize: 24, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: Text( - "By opting out of exposure notifications, your phone will no longer send and recieve anonymous Bluetooth signals to alert you or others of potential exposure to COVID-19.", - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: RoundedButton( - onTap: () { - Analytics.instance.logAlert(text: "Remove My Information", selection: "No"); - Navigator.pop(context); - }, - backgroundColor: Colors.transparent, - borderColor: Styles().colors.fillColorPrimary, - textColor: Styles().colors.fillColorPrimary, - label: Localization().getStringEx("panel.profile_info.dialog.remove_my_information.no.title", "No")), - ), - Container( - width: 10, - ), - Expanded( - child: Stack( - alignment: Alignment.center, - children: [ - RoundedButton( - onTap: () => _onConsentDisabled(context, setState), - backgroundColor: Styles().colors.fillColorSecondaryVariant, - borderColor: Styles().colors.fillColorSecondaryVariant, - textColor: Styles().colors.surface, - label: 'Opt-Out'), - _isDisabling ? Align(alignment: Alignment.center, child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorPrimary), strokeWidth: 2,)) : Container() - ], - ), - ), - ], - ), - ), - ], - ), - ); - }, - ); - } - - void _onConsentDisabled(BuildContext context, StateSetter _setState){ - if(_isDisabling){ - return; - } - _setState((){ - _isDisabling = true; - }); - Health().loginUser(consentExposureNotification: false).whenComplete((){ - _setState((){ - _isDisabling = false; - }); - Navigator.pop(context); - }); - } - - void _onConsentEnabled(){ - if(_isEnabling){ - return; - } - setState((){ - _isEnabling = true; - }); - Health().loginUser(consentExposureNotification: true).whenComplete((){ - setState((){ - _isEnabling = false; - }); - }); - } - - @override - void onNotification(String name, param) { - if(name == Health.notifyUserUpdated){ - setState(() { - - }); - } - } -} \ No newline at end of file diff --git a/lib/ui/settings2/Settings2GovernmentIdPanel.dart b/lib/ui/settings2/Settings2GovernmentIdPanel.dart deleted file mode 100644 index 1a3005ff..00000000 --- a/lib/ui/settings2/Settings2GovernmentIdPanel.dart +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright 2020 Board of Trustees of the University of Illinois. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:illinois/model/UserProfile.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Auth.dart'; -import 'package:illinois/service/Exposure.dart'; -import 'package:illinois/service/Health.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/NativeCommunicator.dart'; -import 'package:illinois/service/Onboarding.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/service/UserProfile.dart'; -import 'package:illinois/ui/widgets/HeaderBar.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; -import 'package:illinois/utils/Utils.dart'; - -class Settings2GovernmentIdPanel extends StatefulWidget with OnboardingPanel { - - final Map initialData; - - Settings2GovernmentIdPanel({this.initialData}); - - _Settings2GovernmentIdPanelPanelState createState() => _Settings2GovernmentIdPanelPanelState(); - - @override - bool get onboardingCanDisplay { - return (onboardingContext != null) && onboardingContext['shouldDisplayReviewScan'] == true; - } -} - -class _Settings2GovernmentIdPanelPanelState extends State { - - static const String kFirstNameFieldName = 'firstName'; - static const String kMiddleNameFieldName = 'middleName'; - static const String kLastNameFieldName = 'lastName'; - static const String kFullNameFieldName = 'fullName'; - - static const String kBirthYearFieldName = 'birthYear'; - - static const String kAddressFieldName = 'address'; - static const String kStateFieldName = 'state'; - static const String kZipFieldName = 'zip'; - static const String kCountryFieldName = 'country'; - static const String kFullAddressFieldName = 'fullAddress'; - - static const String kFaceImageFieldName = 'faceImage'; - static const String kFaceBase64FieldName = 'faceBase64'; - - Map _scanResult; - bool _processingScanResult; - bool _applyingScanResult; - UserDocumentType _documenType; - Map _scanData; - - String _fullName; - String _birthYear; - MemoryImage _photoImage; - - bool _isDeleting = false; - - @override - void initState() { - super.initState(); - - _processingScanResult = false; - _documenType = Auth()?.userPiiData?.documentType; - _scanData = {}; - - _loadInitialData(); - } - - void _loadInitialData(){ - if(widget.initialData != null){ - _scanData = widget.initialData['scanData']; - _documenType = widget.initialData['userDocumentType']; - _loadScanResult(); - } - else{ - _fullName = Auth()?.userPiiData?.fullName; - _birthYear = Auth()?.userPiiData?.birthYear?.toString() ?? ''; - _loadAsyncPhotoBytes(); - } - - } - - Future _deleteUserData() async{ - Analytics.instance.logAlert(text: "Remove My Information", selection: "Yes"); - - await Health().deleteUser(); - await Exposure().deleteUser(); - bool piiDeleted = await Auth().deleteUserPiiData(); - if(piiDeleted) { - await UserProfile().deleteProfile(); - } - Auth().logout(); - } - - Future _loadAsyncPhotoBytes() async { - Uint8List photoBytes = await Auth()?.userPiiData?.photoBytes; - if(AppCollection.isCollectionNotEmpty(photoBytes)){ - _photoImage = await compute(AppImage.memoryImageWithBytes, photoBytes); - setState(() {}); - } - } - - Future _loadScanResult() async{ - compute(_buildScanResult, _scanData).then((Map scanResult) { - setState(() { - _processingScanResult = false; - _scanResult = scanResult; - }); - }); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: SimpleHeaderBarWithBack( - context: context, - titleWidget: Text( - 'Your Government ID', - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontFamily: Styles().fontFamilies.extraBold, - letterSpacing: 1.0, - ), - textAlign: TextAlign.center, - ), - ), - backgroundColor: Styles().colors.background, - body: SafeArea(child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - Expanded(child: - SingleChildScrollView(child: - Column(children: [ - Container(height: 20,), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text('You provided this information to verify your identity during COVID-19 onboarding. You may delete or replace the information below.', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: Styles().fontFamilies.regular, - color: Styles().colors.textSurface, - fontSize: 16 - ), - ), - ), - _buildPreviewWidget(), - ],) - ), - ), - Container(height: 12,), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - children: [ - Expanded( - child: RoundedButton( - label: 'Re-scan', - hint: '', - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - padding: EdgeInsets.symmetric(horizontal: 22), - onTap: () => _onRescan(), - height: 48, - ), - ), - Container(width: 12,), - Expanded( - child: Stack(children: [ - RoundedButton( - label: "Use This Scan", - hint: '', - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - onTap: () => _onUseScan(), - height: 48, - ), - Visibility(visible: (_applyingScanResult == true), - child: Container(height: 48, - child: Align(alignment: Alignment.center, - child: SizedBox(height: 24, width: 24, - child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), ) - ), - ), - ), - ), - ],), - ), - ], - ), - ), - Container(height: 16,), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: RoundedButton( - label: 'Delete my COVID-19 Information', - hint: '', - backgroundColor: Styles().colors.surface, - fontSize: 16.0, - textColor: Styles().colors.fillColorSecondary, - borderColor: Styles().colors.surfaceAccent, - onTap: _onRemoveMyInfoClicked, - - ), - ), - Container(height: 16,) - ],),), - ); - } - - Widget _buildPreviewWidget() { - MemoryImage faceImage = (_scanResult != null) ? _scanResult[kFaceImageFieldName] : _photoImage; - - String nameText = ((_scanResult != null) ? _scanResult[kFullNameFieldName] : _fullName) ?? ''; - String nameLabel = nameText.isNotEmpty ? 'Name' : ''; - - String birthYearText = ((_scanResult != null) ? _scanResult[kBirthYearFieldName] : _birthYear) ?? ''; - String birthYearLabel = birthYearText.isNotEmpty ? 'Birth Year' : ''; - - return Padding(padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), - child: Container( - decoration: BoxDecoration( - color: Styles().colors.white, - borderRadius: BorderRadius.all(Radius.circular(4)), - boxShadow: [BoxShadow(color: Styles().colors.fillColorPrimaryTransparent015, spreadRadius: 2.0, blurRadius: 6.0, offset: Offset(0, 2))], - ), - child: Stack(children: [ - Visibility(visible: (_processingScanResult != true), child: - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container(width: 55, height: 70, - decoration: BoxDecoration( - color: Styles().colors.fillColorPrimaryTransparent03, - borderRadius: BorderRadius.all(Radius.circular(2)), - image: (faceImage != null) ? DecorationImage(fit: BoxFit.cover, alignment: Alignment.center, image: faceImage) : null, - ), - ), - Expanded( - child: Padding(padding: EdgeInsets.only(left: 16), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(nameLabel, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 16, color: Styles().colors.textSurface)), - Text(nameText, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary)), - Container(height: 16,), - Text(birthYearLabel, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 16, color: Styles().colors.textSurface)), - Text(birthYearText, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary)), - ],), - ), - ), - ],), - ), - Visibility(visible: (_processingScanResult == true), - child: Container( - height: 70, - child: Align(alignment: Alignment.center, - child: SizedBox(height: 24, width: 24, - child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), ) - ), - ), - ), - ), - ],) - - - ), - ); - } - - static Map _buildScanResult(Map rawResult) { - - Map rawMrz = rawResult['mrz']; - - String rawFirstName = rawResult['firstName']; // "WILLIAM C III" - if ((rawFirstName == null) && (rawMrz != null)) { - rawFirstName = rawMrz['secondaryID']; // "PETER MARK" - } - String firstName = _buildName(rawFirstName); - String middleName = _buildName(rawFirstName, index: 1); - - String rawLastName = rawResult['lastName']; // "SULLIVAN" - if ((rawLastName == null) && (rawMrz != null)) { - rawLastName = rawMrz['primaryID']; // "HENNESSY" - } - String lastName = _buildName(rawLastName); - - String dateOfBirth = rawResult['dateOfBirth']; // "09/30/1958" - if ((dateOfBirth == null) && (rawMrz != null)) { - dateOfBirth = rawMrz['dateOfBirth']; // "11/22/1960" - } - String birthYear = ((dateOfBirth != null) && RegExp('[0-9]{2}/[0-9]{2}/[0-9]{4}').hasMatch(dateOfBirth)) ? dateOfBirth.substring(6, 10) : null; - - String country; - if (rawMrz != null) { - for (String key in ['sanitizedNationality', 'nationality', 'sanitizedIssuer', 'issuer']) { - String entry = rawMrz[key]; - if ((entry != null) && (0 < entry.length)) { - country = entry; - break; - } - } - } - - String rawAddress = rawResult['address']; // "1804 PLEASANT ST, URBANA, IL, 618010000" - String address = rawAddress, state, zip; - if (rawAddress != null) { - List addressComponents = rawAddress.split(','); - int componentsCount = addressComponents.length; - if ((addressComponents != null) && (1 < componentsCount)) { - - String aZip = addressComponents[componentsCount - 1].trim(); - bool hasZip = RegExp('[0-9]{5,}').hasMatch(aZip); - - String aState = addressComponents[componentsCount - 2].trim(); - bool hasState = RegExp('[a-zA-Z]{2,}').hasMatch(aState); - - if (hasZip && hasState) { - zip = aZip.substring(0, 5); - state = aState; - if (country == null) { - country = 'USA'; - } - - address = ''; - for (int index = 0; (index + 2) < componentsCount; index++) { - if (0 < index) { - address += ','; - } - address += addressComponents[index]; - } - } - } - } - - String fullName = ''; - if ((firstName != null) && (0 < firstName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$firstName"; - } - if ((middleName != null) && (0 < middleName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$middleName"; - } - if ((lastName != null) && (0 < lastName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$lastName"; - } - - String fullAddress = address ?? ''; - if ((state != null) && (zip != null)) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$state $zip"; - } - else if (state != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$state"; - } - else if (zip != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$zip"; - } - if (country != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$country"; - } - - String base64FaceImage = rawResult['base64FaceImage']; - Uint8List faceImageData = (base64FaceImage != null) ? base64Decode(base64FaceImage) : null; - MemoryImage faceImage = (faceImageData != null) ? MemoryImage(faceImageData) : null; - - return { - // These should go to PII - kFirstNameFieldName : firstName, - kMiddleNameFieldName : middleName, - kLastNameFieldName : lastName, - kBirthYearFieldName : birthYear, - kAddressFieldName : address, - kStateFieldName : state, - kZipFieldName : zip, - kCountryFieldName : country, - kFaceBase64FieldName : base64FaceImage, - - // These are for display purpose only - kFullNameFieldName : fullName, - kFullAddressFieldName : fullAddress, - kFaceImageFieldName : faceImage, - }; - } - - static String _buildName(String rawName, {int index = 0}) { - String resultName; - if (rawName != null) { - List firstNameComponents = rawName.split(' '); - if ((firstNameComponents != null) && (0 <= index) && (index < firstNameComponents.length)) { - resultName = firstNameComponents[index]; - } - else if (index == 0) { - resultName = rawName; - } - resultName = (resultName != null) ? AppString.capitalize(resultName) : null; - } - return resultName; - } - - Widget _buildRemoveMyInfoDialog(BuildContext context) { - return StatefulBuilder( - builder: (context, setState){ - return ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), - child: Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Container( - color: Styles().colors.fillColorPrimary, - child: Padding( - padding: EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: Center( - child: Text( - "Remove My Info", - style: TextStyle(fontSize: 20, color: Colors.white), - ), - ), - ), - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all(color: Styles().colors.white, width: 2), - ), - child: Center( - child: Text( - '\u00D7', - style: TextStyle( - fontSize: 24, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: Text( - "By answering YES all your personal information and preferences will be deleted from our systems. This action can not be recovered. After deleting the information we will return you to the first screen when you installed the app so you can start again or delete the app.", - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Container( - height: 26, - ), - Text( - "Are you sure?", - textAlign: TextAlign.center, - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 16, color: Colors.black), - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Stack( - children: [ - RoundedButton( - onTap: () => _onConfirmRemoveMyInfo(context, setState), - backgroundColor: Colors.transparent, - borderColor: Styles().colors.fillColorSecondary, - textColor: Styles().colors.fillColorPrimary, - label: Localization().getStringEx("panel.profile_info.dialog.remove_my_information.yes.title", "Yes")), - _isDeleting ? Align(alignment: Alignment.center, child: CircularProgressIndicator()) : Container() - ], - ), - Container( - height: 10, - ), - RoundedButton( - onTap: () { - Analytics.instance.logAlert(text: "Remove My Information", selection: "No"); - Navigator.pop(context); - }, - backgroundColor: Colors.transparent, - borderColor: Styles().colors.fillColorSecondary, - textColor: Styles().colors.fillColorPrimary, - label: Localization().getStringEx("panel.profile_info.dialog.remove_my_information.no.title", "No")) - ], - ), - ), - ], - ), - ), - ); - }, - ); - } - - Future _applyScan() async { - - UserPiiData updatedUserPiiData; - UserPiiData userPiiData = UserPiiData.fromObject(await Auth().reloadUserPiiData()); - if (userPiiData != null) { - _applyScanResult(userPiiData); - updatedUserPiiData = await Auth().storeUserPiiData(userPiiData); - } - - return (updatedUserPiiData != null); - } - - void _applyScanResult(UserPiiData userPiiData) { - - String photoBase64 = _scanResult[kFaceBase64FieldName]; - if (photoBase64 != null) { - userPiiData.photoBase64 = photoBase64; - } - - String firstName = _scanResult[kFirstNameFieldName]; - if (firstName != null) { - userPiiData.firstName = firstName; - } - - String middleName = _scanResult[kMiddleNameFieldName]; - if (middleName != null) { - userPiiData.middleName = middleName; - } - - String lastName = _scanResult[kLastNameFieldName]; - if (lastName != null) { - userPiiData.lastName = lastName; - } - - String birthYearString = _scanResult[kBirthYearFieldName]; - int birthYear = ((birthYearString != null) && (0 < birthYearString.length)) ? int.tryParse(birthYearString) : null; - if (birthYear != null) { - userPiiData.birthYear = birthYear; - } - - //Don't store this data in PiiData for now -/* String address = _scanResult[kAddressFieldName]; - if ((address != null) && (0 < address.length)) { - userPiiData.address = address; - } - - String state = _scanResult[kStateFieldName]; - if ((state != null) && (0 < state.length)) { - userPiiData.state = state; - } - - String zip = _scanResult[kZipFieldName]; - if ((zip != null) && (0 < zip.length)) { - userPiiData.zip = zip; - } - - String country = _scanResult[kCountryFieldName]; - if ((country != null) && (0 < country.length)) { - userPiiData.country = country; - }*/ - - if (_documenType != null) { - userPiiData.documentType = _documenType; - } - } - - - - void _onRescan() { - Analytics.instance.logSelect(target: 'Re-scan') ; - - String analyticsScanType; - List recognizers; - if (_documenType == UserDocumentType.drivingLicense) { - analyticsScanType = Analytics.LogDocumentScanDrivingLicenseType; - recognizers = ['combined']; - } - else if (_documenType == UserDocumentType.passport) { - analyticsScanType = Analytics.LogDocumentScanPassportType; - recognizers = ['passport']; - } - - NativeCommunicator().microBlinkScan(recognizers: recognizers).then((dynamic result) { - Analytics().logDocumentScan(type: analyticsScanType, result: (result != null)); - if (result != null) { - _didRescan(result); - } - }); - } - - void _didRescan(Map scanData) { - setState(() { - _processingScanResult = true; - }); - compute(_buildScanResult, scanData).then((Map scanResult) { - setState(() { - _processingScanResult = false; - _scanResult = scanResult; - }); - }); - - } - - void _onUseScan() { - Analytics.instance.logSelect(target: 'Use This Scan') ; - - if (_scanResult == null) { - Navigator.pop(context); - } - - setState(() { - _applyingScanResult = true; - }); - - _applyScan().then((bool result){ - - setState(() { - _applyingScanResult = false; - }); - - if (result) { - Navigator.pop(context); - } - else { - AppAlert.showDialogResult(context, 'Failed to apply scanned data'); - } - }); - } - - void _onConfirmRemoveMyInfo(BuildContext context, Function setState){ - setState(() { - _isDeleting = true; - }); - _deleteUserData() - .then((_){ - Navigator.pop(context); - }) - .whenComplete((){ - setState(() { - _isDeleting = false; - }); - }) - .catchError((error){ - AppAlert.showDialogResult(context, error.toString()).then((_){ - Navigator.pop(context); - }); - }); - - - } - - void _onRemoveMyInfoClicked() { - showDialog(context: context, builder: (context) => _buildRemoveMyInfoDialog(context)); - } -} \ No newline at end of file diff --git a/lib/ui/settings2/Settings2HomePanel.dart b/lib/ui/settings2/Settings2HomePanel.dart deleted file mode 100644 index 3735811b..00000000 --- a/lib/ui/settings2/Settings2HomePanel.dart +++ /dev/null @@ -1,845 +0,0 @@ -/* - * Copyright 2020 Board of Trustees of the University of Illinois. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/AppNavigation.dart'; -import 'package:illinois/service/Auth.dart'; -import 'package:illinois/service/Connectivity.dart'; -import 'package:illinois/service/Exposure.dart'; -import 'package:illinois/service/Health.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/Log.dart'; -import 'package:illinois/service/NotificationService.dart'; -import 'package:illinois/service/Organizations.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/service/UserProfile.dart'; -import 'package:illinois/ui/health/HealthHistoryPanel.dart'; -import 'package:illinois/ui/settings2/Settings2TransferEncryptionKeyPanel.dart'; -import 'package:illinois/ui/onboarding/OnboardingResidentInfoPanel.dart'; -import 'package:illinois/ui/onboarding/OnboardingLoginPhoneVerifyPanel.dart'; -import 'package:illinois/ui/settings2/Settings2ConsentPanel.dart'; -import 'package:illinois/ui/settings2/Settings2GovernmentIdPanel.dart'; -import 'package:illinois/ui/settings2/Settings2ExposureNotificationsPanel.dart'; -import 'package:illinois/ui/debug/DebugHomePanel.dart'; -import 'package:illinois/ui/widgets/HeaderBar.dart'; -import 'package:illinois/ui/widgets/RibbonButton.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; -import 'package:illinois/utils/Utils.dart'; -import 'package:intl/intl.dart'; - - -class Settings2HomePanel extends StatefulWidget { - @override - _Settings2HomePanelState createState() => _Settings2HomePanelState(); -} - -class _Settings2HomePanelState extends State implements NotificationsListener { - - bool _isLoading = false; - bool _isDeleting = false; - - @override - void initState() { - super.initState(); - - NotificationService().subscribe(this, [ - Auth.notifyUserPiiDataChanged, - UserProfile.notifyProfileUpdated, - Health.notifyUserUpdated, - ]); - - _loadHealthUser(); - } - - @override - void dispose() { - super.dispose(); - NotificationService().unsubscribe(this); - } - - void _updateState() { - if (mounted) { - setState(() {}); - } - } - - Future _deleteUserData() async{ - Analytics.instance.logAlert(text: "Remove My Information", selection: "Yes"); - - await Health().deleteUser(); - await Exposure().deleteUser(); - bool piiDeleted = await Auth().deleteUserPiiData(); - if(piiDeleted) { - await UserProfile().deleteProfile(); - } - Auth().logout(); - } - - void _loadHealthUser() { - setState(() { - _isLoading = true; - }); - Health().refreshUser().whenComplete((){ - setState(() { - _isLoading = false; - }); - }); - } - - // NotificationsListener - - @override - void onNotification(String name, dynamic param) { - if (name == Auth.notifyUserPiiDataChanged) { - _updateState(); - } else if (name == UserProfile.notifyProfileUpdated){ - _updateState(); - } else if (name == Health.notifyUserUpdated){ - _updateState(); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: SimpleHeaderBarWithBack( - context: context, - titleWidget: _DebugContainer( - child: Text( - Localization().getStringEx("panel.settings.home.settings.header", "Settings"), - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontFamily: Styles().fontFamilies.extraBold, - letterSpacing: 1.0, - ), - textAlign: TextAlign.center, - )), - ), - body: Stack( - children: [ - SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text("About You", - style: TextStyle( - fontFamily: Styles().fontFamilies.extraBold, - color: Styles().colors.fillColorPrimary, - fontSize: 20, - ), - ), - Container(height: 12,), - _buildConnected(), - Container(height: 12,), - CustomRibbonButton( - height: null, - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Add A Government ID', - descriptionLabel: 'Verify your identity by adding a government-issued ID', - leftIcon: 'images/icon-passport.png', - onTap: _onAddGovernmentId, - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'COVID-19 Event History', - descriptionLabel: 'View or delete test results, symptom updates, or contact tracing information', - leftIcon: 'images/icon-identity.png', - onTap: _onEventHistoryTapped, - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Transfer Your COVID-19 Encryption Key', - descriptionLabel: 'View, scan, or save your COVID-19 Encryption Key to transfer to another device.', - leftIcon: 'images/icon-key.png', - onTap: _onTransferKeyTapped, - ), - Container(height: 40,), - Text("Special Consent", - style: TextStyle( - fontFamily: Styles().fontFamilies.extraBold, - color: Styles().colors.fillColorPrimary, - fontSize: 20, - ), - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Exposure Notifications', - value: (Health().user?.consentExposureNotification ?? false) ? 'Enabled' : 'Disabled', - descriptionLabel: 'Learn more information about exposure notifications and manage your settings.', - onTap: _onConsentExposureNotificationsTapped, - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - value: (Health().user?.consentTestResults ?? false) ? 'Enabled' : 'Disabled', - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Automatic Test Results', - descriptionLabel: 'Learn more information about automatic test results and manage your settings.', - onTap: _onConsentTestResultsTapped, - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - value: (Health().user?.consentVaccineInformation ?? false) ? 'Enabled' : 'Disabled', - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Automatic Vaccine Information', - descriptionLabel: 'Learn more information about automatic vaccine information and manage your settings.', - onTap: _onConsentVaccineInformationTapped, - ), - Container(height: 40,), - Text("System Settings", - style: TextStyle( - fontFamily: Styles().fontFamilies.extraBold, - color: Styles().colors.fillColorPrimary, - fontSize: 20, - ), - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - value: 'Disabled', - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Access device’s location', - descriptionLabel: 'To get the most out of our features, enable location in your device’s settings.', - leftIcon: 'images/icon-location-1.png', - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - value: 'Disabled', - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Access device\'s bluetooth', - descriptionLabel: 'To use Bluetooth enable in your device\'s settings.', - leftIcon: 'images/icon-bluetooth.png', - ), - Container(height: 12,), - CustomRibbonButton( - height: null, - value: 'Disabled', - borderRadius: BorderRadius.all(Radius.circular(4)), - label: 'Notifications', - descriptionLabel: 'To receive notifications enable in your device\'s settings.', - leftIcon: 'images/icon-notifications-blue.png', - ), - Container(height: 1, color: Styles().colors.surfaceAccent, margin: EdgeInsets.symmetric(vertical: 20),), - RoundedButton( - label: 'Delete my COVID-19 Information', - hint: '', - backgroundColor: Styles().colors.surface, - fontSize: 16.0, - textColor: Styles().colors.fillColorSecondary, - borderColor: Styles().colors.surfaceAccent, - onTap: _onRemoveMyInfoClicked, - - ), - Padding( - padding: EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Delete your government issued ID information, COVID-19 event history, and encryption key.', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: Styles().fontFamilies.regular, - color: Styles().colors.textBackground, - fontSize: 12 - ), - ), - ) - ], - ), - ), - ), - _isLoading ? Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), strokeWidth: 2,),) : Container() - ], - ) - ); - } - - Widget _buildConnected() { - return Column( - children: [ - _buildConnectedNetIdLayout(), - _buildConnectedPhoneLayout() - ], - ); - } - - Widget _buildConnectedNetIdLayout() { - List contentList = []; - - if(Auth().isShibbolethLoggedIn){ - contentList.add(Container( - width: double.infinity, - decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(4)), border: Border.all(color: Styles().colors.surfaceAccent, width: 0.5)), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Connected as ", - style: TextStyle(color: Styles().colors.textBackground, fontFamily: Styles().fontFamilies.regular, fontSize: 16)), - Text(Auth().userPiiData?.fullName ?? "", - style: TextStyle(color: Styles().colors.fillColorPrimary, fontSize: 20)), - ])))); - contentList.add( - Semantics( explicitChildNodes: true, - child:RibbonButton( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(4), bottomRight: Radius.circular(4)), - border: Border.all(color: Styles().colors.surfaceAccent, width: 0), - label: "Disconnect your NetID", - onTap: _onDisconnectNetIdClicked))); - } - else if(!Auth().isLoggedIn){ - contentList.add( - Semantics( explicitChildNodes: true, - child: RibbonButton( - borderRadius: BorderRadius.all(Radius.circular(4)), - border: Border.all(color: Styles().colors.surfaceAccent, width: 0), - label: "Connect your NetID", - onTap: _onConnectNetIdClicked))); - } - - return Semantics( container:true, - child: Container(child: Column(children: contentList,))); - } - - Widget _buildConnectedPhoneLayout() { - List contentList = []; - - if(Auth().isPhoneLoggedIn){ - String full = Auth()?.userPiiData?.fullName ?? ""; - bool hasFull = AppString.isStringNotEmpty(full); - - contentList.add(Container( - width: double.infinity, - decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(4)), border: Border.all(color: Styles().colors.surfaceAccent, width: 0.5)), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Verified as ", - style: TextStyle(color: Styles().colors.textBackground, fontFamily: Styles().fontFamilies.regular, fontSize: 16)), - Visibility(visible: hasFull, child: Text(full ?? "", style: TextStyle(color: Styles().colors.fillColorPrimary, fontSize: 20)),), - Text(Auth().phoneToken?.phone ?? "", style: TextStyle(color: Styles().colors.fillColorPrimary, fontSize: 20)), - ])))); - contentList.add( - Semantics( explicitChildNodes: true, - child:RibbonButton( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(4), bottomRight: Radius.circular(4)), - border: Border.all(color: Styles().colors.surfaceAccent, width: 0), - label: "Disconnect your Phone", - onTap: _onDisconnectNetIdClicked))); - } - else if(!Auth().isLoggedIn){ - contentList.add( - Semantics( explicitChildNodes: true, - child:RibbonButton( - borderRadius:BorderRadius.all(Radius.circular(4)), - border: Border.all(color: Styles().colors.surfaceAccent, width: 0), - label: "Verify Your Phone Number", - onTap: _onPhoneVerClicked))); - } - return Column(children: contentList,); - } - - Widget _buildRemoveMyInfoDialog(BuildContext context) { - return StatefulBuilder( - builder: (context, setState){ - return ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), - child: Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: Styles().colors.fillColorPrimary, - borderRadius: BorderRadius.vertical(top: Radius.circular(8)), - ), - child: Padding( - padding: EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: Center( - child: Text( - "Delete your COVID-19 event history?", - style: TextStyle(fontSize: 20, color: Colors.white), - ), - ), - ), - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all(color: Styles().colors.white, width: 2), - ), - child: Center( - child: Text( - '\u00D7', - style: TextStyle( - fontSize: 24, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ), - Container( - height: 26, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: Text( - "This will permanently delete all of your COVID-19 event history information. Are you sure you want to continue?", - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Container( - height: 26, - ), - Text( - "Are you sure?", - textAlign: TextAlign.center, - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 16, color: Colors.black), - ), - Container( - height: 16, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: RoundedButton( - onTap: () { - Analytics.instance.logAlert(text: "Remove My Information", selection: "No"); - Navigator.pop(context); - }, - backgroundColor: Colors.transparent, - borderColor: Styles().colors.fillColorPrimary, - textColor: Styles().colors.fillColorPrimary, - label: 'No'), - ), - Container( - width: 10, - ), - Expanded( - child: Stack( - children: [ - RoundedButton( - onTap: () => _onConfirmRemoveMyInfo(context, setState), - backgroundColor: Styles().colors.fillColorSecondaryVariant, - borderColor: Styles().colors.fillColorSecondaryVariant, - textColor: Styles().colors.surface, - label: Localization().getStringEx("panel.profile_info.dialog.remove_my_information.yes.title", "Yes")), - _isDeleting ? Align(alignment: Alignment.center, child: CircularProgressIndicator()) : Container() - ], - ), - ), - ], - ), - ), - ], - ), - ), - ); - }, - ); - } - - Widget _buildLogoutDialog(BuildContext context) { - return Dialog( - child: Padding( - padding: EdgeInsets.all(18), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Safer Illinois", - style: TextStyle(fontSize: 24, color: Colors.black), - ), - Padding( - padding: EdgeInsets.symmetric(vertical: 26), - child: Text( - "Are you sure you want to sign out?", - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - Analytics.instance.logAlert(text: "Sign out", selection: "Yes"); - Navigator.pop(context); - Auth().logout(); - }, - child: Text("Yes")), - TextButton( - onPressed: () { - Analytics.instance.logAlert(text: "Sign out", selection: "No"); - Navigator.pop(context); - }, - child: Text("No")) - ], - ), - ], - ), - ), - ); - } - - void _onConnectNetIdClicked() { - Analytics.instance.logSelect(target: "Connect netId"); - Auth().authenticateWithShibboleth(); - } - - void _onDisconnectNetIdClicked() { - if(Auth().isShibbolethLoggedIn) { - Analytics.instance.logSelect(target: "Disconnect netId"); - } else { - Analytics.instance.logSelect(target: "Disconnect phone"); - } - showDialog(context: context, builder: (context) => _buildLogoutDialog(context)); - } - - void _onPhoneVerClicked() { - Analytics.instance.logSelect(target: "Phone Verification"); - if (Connectivity().isNotOffline) { - Navigator.push(context, CupertinoPageRoute(settings: RouteSettings(), builder: (context) => OnboardingLoginPhoneVerifyPanel(onFinish: _didPhoneVer,))); - } else { - AppAlert.showOfflineMessage(context, 'Verify Your Phone Number is not available while offline.'); - } - } - - void _onAddGovernmentId(){ - if(Auth()?.userPiiData?.hasPasportInfo ?? false){ - Navigator.push(context, CupertinoPageRoute( - builder: (context) => Settings2GovernmentIdPanel() - )); - } - else { - Navigator.push(context, CupertinoPageRoute( - builder: (context) => OnboardingResidentInfoPanel( - onSucceed: (Map data){ - Navigator.pushReplacement(context, CupertinoPageRoute(builder: (context) => Settings2GovernmentIdPanel(initialData: data,))); - }, - onCancel: ()=>Navigator.pop(context), - ) - )); - } - } - - void _onRemoveMyInfoClicked() { - showDialog(context: context, builder: (context) => _buildRemoveMyInfoDialog(context)); - } - - void _didPhoneVer(_) { - Navigator.of(context)?.popUntil((Route route){ - return AppNavigation.routeRootWidget(route, context: context)?.runtimeType == widget.runtimeType; - }); - } - - void _onConfirmRemoveMyInfo(BuildContext context, Function setState){ - setState(() { - _isDeleting = true; - }); - _deleteUserData() - .then((_){ - Navigator.pop(context); - }) - .whenComplete((){ - setState(() { - _isDeleting = false; - }); - }) - .catchError((error){ - AppAlert.showDialogResult(context, error.toString()).then((_){ - Navigator.pop(context); - }); - }); - - - } - - void _onEventHistoryTapped(){ - Analytics.instance.logSelect(target: "COVID-19 Test History"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => HealthHistoryPanel())); - } - - void _onTransferKeyTapped() { - Analytics.instance.logSelect(target: "Transfer Your COVID-19 Encryption Key"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => Settings2TransferEncryptionKeyPanel())); - } - - void _onConsentExposureNotificationsTapped(){ - Analytics.instance.logSelect(target: "Consent Exposure Notifications"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => Settings2ExposureNotificationsPanel())); - } - - void _onConsentTestResultsTapped(){ - Analytics.instance.logSelect(target: "Consent Test Results"); - Navigator.push(context, CupertinoPageRoute(builder: (context) => Settings2ConsentPanel())); - } - - void _onConsentVaccineInformationTapped(){ - Analytics.instance.logSelect(target: "Consent Vaccine Information"); - //TBD: Navigator.push(context, CupertinoPageRoute(builder: (context) => Settings2ConsentPanel())); - } - -} - -class CustomRibbonButton extends StatelessWidget { - final String label; - final String value; - final String descriptionLabel; - - final GestureTapCallback onTap; - final EdgeInsets padding; - final BorderRadius borderRadius; - final BoxBorder border; - final TextStyle style; - final double height; - final String leftIcon; - final String icon; - final BuildContext context; - final String hint; - - CustomRibbonButton({ - @required this.label, - this.value, - this.descriptionLabel, - this.onTap, - this.borderRadius = BorderRadius.zero, - this.border, - this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - this.style, - this.height = 48.0, - this.icon = 'images/chevron-right.png', - this.leftIcon, - this.context, - this.hint, - }); - - @override - Widget build(BuildContext context) { - return getSemantics(); - } - - Semantics getSemantics() { - return Semantics(label: label, hint : hint, button: true, excludeSemantics: true, child: _content()); - } - - Widget _content() { - bool hasDescription = AppString.isStringNotEmpty(descriptionLabel); - bool hasValue = AppString.isStringNotEmpty(value); - Widget image = getImage(); - Widget leftIconWidget = AppString.isStringNotEmpty(leftIcon) ? Padding(padding: EdgeInsets.only(right: 7), child: Image.asset(leftIcon)) : Container(); - Widget leftIconHiddenWidget = Opacity(opacity: 0, child: AppString.isStringNotEmpty(leftIcon) ? Padding(padding: EdgeInsets.only(right: 7), child: Image.asset(leftIcon)) : Container(),); - return GestureDetector( - onTap: () { onTap(); anaunceChange(); }, - child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Container( - decoration: BoxDecoration(color: Colors.white, border:border, borderRadius: borderRadius, boxShadow: [ - BoxShadow( - color: Styles().colors.lightGray, - spreadRadius: 3, - blurRadius: 3, - offset: Offset(2, 2), // changes position of shadow - ), - ]), - height: this.height, - child: Padding( - padding: padding, - child: Column( - children: [ - Row( - children: [ - leftIconWidget, - Expanded(child: - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label, - style: style ?? TextStyle(color: Styles().colors.fillColorPrimary, fontSize: 16, fontFamily: Styles().fontFamilies.bold), - ), - ], - ) - ), - (image != null) ? Padding(padding: EdgeInsets.only(left: 7), child: image) : Container(), - ], - ), - hasValue ? Row( - children: [ - leftIconHiddenWidget, - Expanded(child: hasValue ? Container( - child: Text(value, - style: style ?? TextStyle(color: Styles().colors.textSurface, fontSize: 14, fontFamily: Styles().fontFamilies.regular), - ), - ) : Container() - ,) - ], - ) : Container(), - Row( - children: [ - leftIconHiddenWidget, - Expanded(child: hasDescription ? Container( - margin: EdgeInsets.only(top: 4), - child: Text(descriptionLabel, - style: style ?? TextStyle(color: Styles().colors.textSurface, fontSize: 14, fontFamily: Styles().fontFamilies.regular), - ), - ) : Container() - ,) - ], - ), - ], - ), - ), - ) - ),],), - ); - } - - Widget getImage() { - return (icon != null) ? Image.asset(icon) : null; - } - - void anaunceChange() {} -} - -class _DebugContainer extends StatefulWidget { - - final Widget _child; - - _DebugContainer({@required Widget child}) : _child = child; - - _DebugContainerState createState() => _DebugContainerState(); -} - -class _DebugContainerState extends State<_DebugContainer> { - - int _clickedCount = 0; - - @override - Widget build(BuildContext context) { - return GestureDetector( - child: widget._child, - onTap: () { - Log.d("On tap debug widget"); - _clickedCount++; - - if (_clickedCount == 7) { - _showPinDialog(); - _clickedCount = 0; - } - }, - ); - } - - void _showPinDialog(){ - TextEditingController pinController = TextEditingController(text: (!kReleaseMode || Organizations().isDevEnvironment) ? this.pinOfTheDay : ''); - showDialog(context: context, barrierDismissible: false, builder: (context) => Dialog( - child: Padding( - padding: EdgeInsets.all(18), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - Localization().getStringEx('app.title', 'Safer Illinois'), - style: TextStyle(fontSize: 24, color: Colors.black), - ), - Padding( - padding: EdgeInsets.symmetric(vertical: 26), - child: Text( - Localization().getStringEx('panel.debug.label.pin', 'Please enter pin'), - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.medium, fontSize: 16, color: Colors.black), - ), - ), - Container(height: 6,), - TextField(controller: pinController, autofocus: true, keyboardType: TextInputType.number, obscureText: true, - onSubmitted:(String value){ - _onEnterPin(value); - } - ,), - Container(height: 6,), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - Navigator.pop(context); - //_finish(); - }, - child: Text(Localization().getStringEx('dialog.cancel.title', 'Cancel'))), - Container(width: 6), - TextButton( - onPressed: () { - _onEnterPin(pinController?.text); - //_finish(); - }, - child: Text(Localization().getStringEx('dialog.ok.title', 'OK'))) - ], - ) - ], - ), - ), - )); - } - - String get pinOfTheDay { - return DateFormat('MMdd').format(DateTime.now()); - } - - void _onEnterPin(String pin){ - if (this.pinOfTheDay == pin) { - Navigator.pop(context); - Navigator.push(context, CupertinoPageRoute(builder: (context) => DebugHomePanel())); - } else { - AppToast.show("Invalid pin"); - } - } -} \ No newline at end of file diff --git a/lib/ui/settings2/Settings2TransferEncryptionKeyPanel.dart b/lib/ui/settings2/Settings2TransferEncryptionKeyPanel.dart deleted file mode 100644 index 9c820bf6..00000000 --- a/lib/ui/settings2/Settings2TransferEncryptionKeyPanel.dart +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 2020 Board of Trustees of the University of Illinois. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'dart:typed_data'; - -import 'package:barcode_scan/barcode_scan.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Health.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/NativeCommunicator.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/ui/widgets/HeaderBar.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; -import 'package:illinois/utils/Covid19.dart'; -import 'package:illinois/utils/Crypt.dart'; -import 'package:illinois/utils/Utils.dart'; -import 'package:pointycastle/export.dart' as PointyCastle; - -class Settings2TransferEncryptionKeyPanel extends StatefulWidget { - - const Settings2TransferEncryptionKeyPanel({Key key}) : super(key: key); - @override - _Settings2TransferEncryptionKeyPanelState createState() => _Settings2TransferEncryptionKeyPanelState(); -} - -class _Settings2TransferEncryptionKeyPanelState extends State { - - PointyCastle.PublicKey _userPublicKey; - PointyCastle.PrivateKey _userPrivateKey; - bool _prepairing, _userKeysPaired; - Uint8List _qrCodeBytes; - bool _saving = false; - - @override - void initState() { - super.initState(); - - _prepairing = true; - _userPublicKey = Health().user?.publicKey; - _userPrivateKey = Health().userPrivateKey; - _verifyHealthRSAKeys(); - } - - void _verifyHealthRSAKeys() { - - if ((_userPrivateKey != null) && (_userPublicKey != null)) { - RsaKeyHelper.verifyRsaKeyPair(PointyCastle.AsymmetricKeyPair(_userPublicKey, _userPrivateKey)).then((bool result) { - if (mounted) { - _userKeysPaired = result; - _buildHealthRSAQRCode(); - } - }); - } - else { - _finishPrepare(); - } - } - - void _buildHealthRSAQRCode() { - if (_userKeysPaired && (_userPrivateKey != null)) { - RsaKeyHelper.compressRsaPrivateKey(_userPrivateKey).then((String privateKeyString) { - if (mounted) { - if (privateKeyString != null) { - NativeCommunicator().getBarcodeImageData({ - 'content': privateKeyString, - 'format': 'qrCode', - 'width': 1024, - 'height': 1024, - }).then((Uint8List qrCodeBytes) { - if (mounted) { - _qrCodeBytes = qrCodeBytes; - _finishPrepare(); - } - }); - } - else { - _finishPrepare(); - } - } - }); - } - else { - _finishPrepare(); - } - } - - void _finishPrepare() { - setState(() { - _prepairing = false; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: SimpleHeaderBarWithBack( - context: context, - titleWidget: Text( - Localization().getStringEx('panel.covid19.transfer.title', 'Transfer Encryption Key'), - style: TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w900, - letterSpacing: 1.0, - ), - textAlign: TextAlign.center, - ), - ), - body: Column(children: [ - Expanded(child: - Padding(padding: EdgeInsets.all(24), child: - (_prepairing == true) ? _buildWaitingContent() : _buildPrivateKeyContent() - ), - ), - ],), - backgroundColor: Styles().colors.background, - ); - } - - Widget _buildWaitingContent() { - return Center(child: - CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), strokeWidth: 2,) - ); - } - - Widget _buildPrivateKeyContent(){ - return SingleChildScrollView( - child: (_qrCodeBytes != null) ? _buildQrCodeContent() : _buildNoQrCodeContent(), - ); - } - - Widget _buildQrCodeContent(){ - return Column( children: [ - Container(height: 15,), - Semantics( header: true, hint: Localization().getStringEx("app.common.heading.one.hint","Header 1"), - child:Text(Localization().getStringEx("panel.covid19.transfer.primary.heading.title", "Your COVID-19 Encryption Key"), - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 28, color:Styles().colors.fillColorPrimary), - )), - Container(height: 30,), - _buildQrCode(), - Container(height: 20,), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: RoundedButton( - label: Localization().getStringEx("panel.covid19.transfer.primary.button.save.title", "Save Your Encryption Key"), - hint: Localization().getStringEx("panel.covid19.transfer.primary.button.save.hint", ""), - borderColor: Styles().colors.fillColorSecondaryVariant, - backgroundColor: Styles().colors.surface, - fontSize: 16, - height: 40, - padding: EdgeInsets.symmetric(vertical: 5), - textColor: Styles().colors.fillColorPrimary, - onTap: _onSaveImage, - ), - ), - Container(height: 30,) - ], - ); - } - - Widget _buildNoQrCodeContent(){ - return Column( children: [ - Container(height: 15,), - Semantics( header: true, hint: Localization().getStringEx("app.common.heading.one.hint","Header 1"), - child:Text(Localization().getStringEx("panel.covid19.transfer.secondary.heading.title", "Missing COVID-19 Encryption Key"), - textAlign: TextAlign.left, - style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 28, color:Styles().colors.fillColorPrimary), - )), - Container(height: 30,), - _buildAction( - heading: Localization().getStringEx("panel.covid19.transfer.secondary.button.scan.heading", "If you are adding a second device:"), - description: Localization().getStringEx("panel.covid19.transfer.secondary.button.scan.description", "If you still have access to your primary device, you can directly scan the COVID-19 Encryption Key QR code from that device."), - title: Localization().getStringEx("panel.covid19.transfer.secondary.button.scan.title", "Scan Your QR Code"), - iconRes: "images/fill-1.png", - onTap: _onScan - ), - Container(height: 12,), - _buildAction( - heading: Localization().getStringEx("panel.covid19.transfer.secondary.button.retrieve.heading", "If you are using a replacement device:"), - description: Localization().getStringEx("panel.covid19.transfer.secondary.button.retrieve.description", "If you no longer have access to your primary device, but saved your QR code to a cloud photo service, you can transfer your COVID-19 Encryption Key by retrieving it from your photos."), - title: Localization().getStringEx("panel.covid19.transfer.secondary.button.retrieve.title", "Retrieve Your QR Code"), - iconRes: "images/group-10.png", - onTap: _onRetrieve - ), - Container(height: 12,), - Container(height: 40,) - ], -); - } - - Widget _buildQrCode(){ - return Container( - decoration: BoxDecoration( - color: Styles().colors.white, - borderRadius: BorderRadius.all( Radius.circular(5))), - padding: EdgeInsets.all(1), - child: Semantics(child: - Image.memory(_qrCodeBytes, fit: BoxFit.fitWidth, semanticLabel: Localization().getStringEx("panel.covid19.transfer.primary.heading.title", "Your COVID-19 Encryption Key"), - )), - ); - } - - Widget _buildAction({String heading, String description, String title, String iconRes, Function onTap}){ - return Semantics(container: true, child:Container( - decoration: BoxDecoration( - color: Styles().colors.white, - borderRadius: BorderRadius.all( Radius.circular(5))), - child: Column( - children: [ - Container(height: 18,), - Container( padding: EdgeInsets.symmetric(horizontal: 20), - child: Text(heading, style: TextStyle(fontFamily: Styles().fontFamilies.bold, fontSize: 16, color:Styles().colors.fillColorPrimary))), - Container(height: 9,), - Container( padding: EdgeInsets.symmetric(horizontal: 20), - child: Text(description, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 14, color:Styles().colors.fillColorPrimary))), - Container(height: 14,), - Semantics( - explicitChildNodes: true, - child: Container(child: - GestureDetector( - onTap: onTap, - child:Container( - decoration: BoxDecoration( - color: Styles().colors.background, - borderRadius: BorderRadius.only( bottomLeft: Radius.circular(5), bottomRight: Radius.circular(5)), - border: Border.all(color: Styles().colors.surfaceAccent,) - ), - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15), - child: Row( - children: [ - Image.asset(iconRes, excludeFromSemantics: true,), - Container(width: 7,), - Semantics(button: true, excludeSemantics:false, child: - Text(title, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 14, color:Styles().colors.fillColorPrimary))), - Expanded(child: Container(),), - Image.asset('images/chevron-right.png',excludeFromSemantics: true,), - ], - ))))), - ], - ) - )); - } - - void _onSaveImage(){ - Analytics.instance.logSelect(target: "Save Your Encryption Key"); - if(!_saving) { - setState(() { - _saving = true; - }); - Covid19Utils.saveQRCodeImageToPictures(qrCodeBytes: _qrCodeBytes, title: Localization().getStringEx("panel.covid19.transfer.label.qr_image_label", "Safer Illinois COVID-19 Code")).then((bool result) { - setState(() { - _saving = false; - }); - String platformTargetText = (defaultTargetPlatform == TargetPlatform.android) ? Localization().getStringEx("panel.covid19.transfer.alert.save.success.pictures", "Pictures") : Localization().getStringEx("panel.covid19.transfer.alert.save.success.gallery", "Gallery"); - String message = result - ? (Localization().getStringEx("panel.covid19.transfer.alert.save.success.msg", "Successfully saved qr code in ") + platformTargetText) - : Localization().getStringEx("panel.covid19.transfer.alert.save.fail.msg", "Failed to save qr code in ") + platformTargetText; - AppAlert.showDialogResult(context, message); - }); - } - } - - void _onScan(){ - Analytics.instance.logSelect(target: "Scan Your QR Code"); - BarcodeScanner.scan().then((result) { - // barcode_scan plugin returns 8 digits when it cannot read the qr code. Prevent it from storing such values - if (AppString.isStringEmpty(result?.rawContent) || ((result?.rawContent?.length ?? 0) <= 8)) { - AppAlert.showDialogResult(context, Localization().getStringEx('panel.covid19.transfer.alert.qr_code.scan.failed.msg', 'Failed to read QR code.')); - } - else { - _onCovid19QrCodeScanSucceeded(result?.rawContent); - } - }); - } - - void _onRetrieve() { - Analytics.instance.logSelect(target: "Retrieve Your QR Code"); - Covid19Utils.loadQRCodeImageFromPictures().then((String qrCodeString) { - _onCovid19QrCodeScanSucceeded(qrCodeString); - }); - } - - void _onCovid19QrCodeScanSucceeded(String result) { - - RsaKeyHelper.decompressRsaPrivateKey(result).then((PointyCastle.PrivateKey privateKey) { - if (mounted) { - if (privateKey != null) { - RsaKeyHelper.verifyRsaKeyPair(PointyCastle.AsymmetricKeyPair(_userPublicKey, privateKey)).then((bool result) { - if (mounted) { - if (result == true) { - Health().setUserPrivateKey(privateKey).then((success) { - if (mounted) { - String resultMessage = success ? - Localization().getStringEx("panel.covid19.transfer.alert.qr_code.transfer.succeeded.msg", "COVID-19 secret transferred successfully.") : - Localization().getStringEx("panel.covid19.transfer.alert.qr_code.transfer.failed.msg", "Failed to transfer COVID-19 secret."); - AppAlert.showDialogResult(context, resultMessage); - } - }); - } - else { - AppAlert.showDialogResult(context, Localization().getStringEx('panel.covid19.transfer.alert.qr_code.not_match.msg', 'COVID-19 secret key does not match existing public RSA key.')); - } - } - }); - } - else { - AppAlert.showDialogResult(context, Localization().getStringEx('panel.covid19.transfer.alert.qr_code.invalid.msg', 'Invalid QR code.')); - } - } - }); - } - -} From e4ed1fe6d19ab98eca834340b014fc83f9571b36 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 11:23:35 +0300 Subject: [PATCH 68/73] Removed OnboardingResidentInfo and OnboardingReviewScan panels - unused (#717). --- lib/service/Onboarding.dart | 10 - .../OnboardingResidentInfoPanel.dart | 188 ------- .../onboarding/OnboardingReviewScanPanel.dart | 492 ------------------ 3 files changed, 690 deletions(-) delete mode 100644 lib/ui/onboarding/OnboardingResidentInfoPanel.dart delete mode 100644 lib/ui/onboarding/OnboardingReviewScanPanel.dart diff --git a/lib/service/Onboarding.dart b/lib/service/Onboarding.dart index 023f805f..911c6edd 100644 --- a/lib/service/Onboarding.dart +++ b/lib/service/Onboarding.dart @@ -26,8 +26,6 @@ import 'package:illinois/ui/onboarding/OnboardingHealthFinalPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingHealthHowItWorksPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingHealthIntroPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingHealthQrCodePanel.dart'; -import 'package:illinois/ui/onboarding/OnboardingResidentInfoPanel.dart'; -import 'package:illinois/ui/onboarding/OnboardingReviewScanPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingAuthBluetoothPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingLoginPhoneConfirmPanel.dart'; import 'package:illinois/ui/onboarding/OnboardingGetStartedPanel.dart'; @@ -164,8 +162,6 @@ class Onboarding extends Service implements NotificationsListener{ case "login_phone": return OnboardingLoginPhonePanel(onboardingContext: context); case "verify_phone": return OnboardingLoginPhoneVerifyPanel(onboardingContext: context); case "confirm_phone": return OnboardingLoginPhoneConfirmPanel(onboardingContext: context); - case "resident_info": return OnboardingResidentInfoPanel(onboardingContext: context); - case "review_scan": return OnboardingReviewScanPanel(onboardingContext: context); case "health_intro": return OnboardingHealthIntroPanel(onboardingContext: context); case "health_how_it_works": return OnboardingHealthHowItWorksPanel(onboardingContext: context); case "health_disclosure": return OnBoardingHealthDisclosurePanel(onboardingContext: context); @@ -207,12 +203,6 @@ class Onboarding extends Service implements NotificationsListener{ else if (panel is OnboardingLoginPhoneConfirmPanel) { return 'confirm_phone'; } - else if (panel is OnboardingResidentInfoPanel) { - return 'resident_info'; - } - else if (panel is OnboardingReviewScanPanel) { - return 'review_scan'; - } else if (panel is OnboardingHealthIntroPanel) { return 'health_intro'; } diff --git a/lib/ui/onboarding/OnboardingResidentInfoPanel.dart b/lib/ui/onboarding/OnboardingResidentInfoPanel.dart deleted file mode 100644 index 7c264f76..00000000 --- a/lib/ui/onboarding/OnboardingResidentInfoPanel.dart +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2020 Board of Trustees of the University of Illinois. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:illinois/model/UserProfile.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Auth.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/NativeCommunicator.dart'; -import 'package:illinois/service/Onboarding.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/service/UserProfile.dart'; -import 'package:illinois/ui/onboarding/OnboardingHealthProgress.dart'; -import 'package:illinois/ui/onboarding/OnboardingBackButton.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; -import 'package:illinois/ui/widgets/ScalableScrollView.dart'; - -class OnboardingResidentInfoPanel extends StatelessWidget with OnboardingPanel { - - final Map onboardingContext; - final Function(Map) onSucceed; - final Function onCancel; - - OnboardingResidentInfoPanel({this.onboardingContext, this.onSucceed, this.onCancel}); - - @override - bool get onboardingCanDisplay { - return !UserProfile().isStudentOrEmployee && (onboardingContext != null) && onboardingContext['shouldDisplayResidentInfo'] == true; - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Styles().colors.background, - body: ScalableScrollView( - scrollableChild: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container(color: Styles().colors.white, child: Stack(children: [ - OnboardingHealthProgress(progress: 0.50,), - Align(alignment: Alignment.topLeft, - child: OnboardingBackButton(image: 'images/chevron-left-blue.png', padding: EdgeInsets.only(top: 16, right: 20, bottom: 20), onTap: () => _goBack(context)), - ), - Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24, bottom: 12), - child: Semantics( header: true, hint: Localization().getStringEx("app.common.heading.one.hint","Header 1"), - child:Text( Localization().getStringEx('panel.health.onboarding.covid19.resident_info.label.title', 'Verify your identity with a government-issued ID',), - textAlign: TextAlign.center, - style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary), - ))), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, bottom: 19), - child: Text( - Localization().getStringEx('panel.health.onboarding.covid19.resident_info.label.description', 'After verifying you will receive a color-coded health status based on your county guidelines, symptoms, and any COVID-19 related tests.'), - textAlign: TextAlign.center, - style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 16, color: Styles().colors.fillColorPrimary), - )), - ],) - ],),),]), - bottomNotScrollableWidget: Padding( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding(padding: EdgeInsets.symmetric(vertical: 17, horizontal: 16), child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded(child:ScalableRoundedButton( - label: Localization().getStringEx('panel.health.onboarding.covid19.resident_info.button.passport.title', 'Passport'), - hint: Localization().getStringEx('panel.health.onboarding.covid19.resident_info.button.passport.hint', ''), - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - padding: EdgeInsets.symmetric(horizontal: 22), - onTap: () => _doScan(context, UserDocumentType.passport), - )), - Container(width: 16,), - Expanded(child: ScalableRoundedButton( - label: Localization().getStringEx('panel.health.onboarding.covid19.resident_info.button.drivers_license.title', "Driver's License"), - hint: Localization().getStringEx('panel.health.onboarding.covid19.resident_info.button.drivers_license.hint', ''), - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - onTap: () => _doScan(context, UserDocumentType.drivingLicense), - ),) - ],),), - GestureDetector( - onTap: () => _onTapVerifyLater(context), - behavior: HitTestBehavior.translucent, - child: Container( - child: Padding( - padding: EdgeInsets.only(bottom: 20), - child: Text( - Localization().getStringEx('panel.health.onboarding.covid19.resident_info.button.verify_later.title', "Verify later"), - style: TextStyle( - fontFamily: Styles().fontFamilies.regular, - fontSize: 16, - color: Styles().colors.fillColorPrimary, - decoration: TextDecoration.underline, - decorationColor: Styles().colors.fillColorSecondary, - decorationThickness: 1, - decorationStyle: TextDecorationStyle.solid), - )), - ), - ) - ], - ),) - )); - } - - void _goBack(BuildContext context) { - Analytics.instance.logSelect(target: "Back"); - Navigator.of(context).pop(); - } - - void _doScan(BuildContext context, UserDocumentType documentType) { - - String analyticsScanType; - List recognizers; - if (documentType == UserDocumentType.drivingLicense) { - Analytics.instance.logSelect(target: "Driver's License") ; - analyticsScanType = Analytics.LogDocumentScanDrivingLicenseType; - recognizers = ['combined']; - } - else if (documentType == UserDocumentType.passport) { - Analytics.instance.logSelect(target: 'Passport') ; - analyticsScanType = Analytics.LogDocumentScanPassportType; - recognizers = ['passport']; - } - - NativeCommunicator().microBlinkScan(recognizers: recognizers).then((dynamic result) { - Analytics().logDocumentScan(type: analyticsScanType, result: (result != null)); - if (result != null) { - _didScan(context, documentType, result); - } - }); - } - - void _didScan(BuildContext context, UserDocumentType documentType, Map scanData) { - if(onboardingContext != null) { - onboardingContext['shouldDisplayReviewScan'] = true; - onboardingContext['userDocumentType'] = documentType; - onboardingContext['scanData'] = scanData; - Onboarding().next(context, this); - } - else if(onSucceed != null){ - onSucceed({ - 'userDocumentType': documentType, - 'scanData': scanData - }); - } - else{ - Navigator.pop(context); - } - } - - void _onTapVerifyLater(BuildContext context) { - if(onboardingContext != null) { - Analytics.instance.logSelect(target: 'Verify later'); - onboardingContext['shouldDisplayReviewScan'] = false; - if (Auth().isLoggedIn) { - onboardingContext['shouldDisplayQrCode'] = true; - } else { - onboardingContext['shouldDisplayQrCode'] = false; - } - Onboarding().next(context, this); - } - else if(onCancel != null){ - onCancel(); - } - else{ - Navigator.pop(context); - } - } -} diff --git a/lib/ui/onboarding/OnboardingReviewScanPanel.dart b/lib/ui/onboarding/OnboardingReviewScanPanel.dart deleted file mode 100644 index e6ccd01e..00000000 --- a/lib/ui/onboarding/OnboardingReviewScanPanel.dart +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright 2020 Board of Trustees of the University of Illinois. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:illinois/model/UserProfile.dart'; -import 'package:illinois/service/Analytics.dart'; -import 'package:illinois/service/Auth.dart'; -import 'package:illinois/service/Localization.dart'; -import 'package:illinois/service/NativeCommunicator.dart'; -import 'package:illinois/service/Onboarding.dart'; -import 'package:illinois/service/Styles.dart'; -import 'package:illinois/service/UserProfile.dart'; -import 'package:illinois/ui/onboarding/OnboardingHealthProgress.dart'; -import 'package:illinois/ui/onboarding/OnboardingBackButton.dart'; -import 'package:illinois/ui/widgets/RoundedButton.dart'; -import 'package:illinois/utils/Utils.dart'; - -class OnboardingReviewScanPanel extends StatefulWidget with OnboardingPanel { - - final Map onboardingContext; - - OnboardingReviewScanPanel({this.onboardingContext}); - - _OnboardingReviewScanPanelState createState() => _OnboardingReviewScanPanelState(); - - @override - bool get onboardingCanDisplay { - return !UserProfile().isStudentOrEmployee && (onboardingContext != null) && onboardingContext['shouldDisplayReviewScan'] == true; - } -} - -class _OnboardingReviewScanPanelState extends State { - - static const String kFirstNameFieldName = 'firstName'; - static const String kMiddleNameFieldName = 'middleName'; - static const String kLastNameFieldName = 'lastName'; - static const String kFullNameFieldName = 'fullName'; - - static const String kBirthYearFieldName = 'birthYear'; - - static const String kAddressFieldName = 'address'; - static const String kStateFieldName = 'state'; - static const String kZipFieldName = 'zip'; - static const String kCountryFieldName = 'country'; - static const String kFullAddressFieldName = 'fullAddress'; - - /*static const String kHomeCountyFieldName = 'homeCounty'; - static const String kWorkCountyFieldName = 'workCounty'; - static const String kProvidersFieldName = 'providers'; - static const String kConsentFieldName = 'consent';*/ - - static const String kFaceImageFieldName = 'faceImage'; - static const String kFaceBase64FieldName = 'faceBase64'; - - Map _scanResult; - bool _processingScanResult; - bool _applyingScanResult; - UserDocumentType _documenType; - Map _scanData; - - @override - void initState() { - _processingScanResult = true; - _documenType = widget.onboardingContext['userDocumentType']; - _scanData = widget.onboardingContext['scanData']; - compute(_buildScanResult, _scanData).then((Map scanResult) { - setState(() { - _processingScanResult = false; - _scanResult = scanResult; - }); - }); - - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Styles().colors.background, - body: SafeArea(child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - Container(color: Styles().colors.white, child: Stack(children: [ - OnboardingHealthProgress(progress: 0.75,), - Align(alignment: Alignment.topLeft, - child: OnboardingBackButton(image: 'images/chevron-left-blue.png', padding: EdgeInsets.only(top: 16, right: 20, bottom: 20), onTap: () => _goBack()), - ), - Align(alignment: Alignment.topCenter, child: - Padding(padding: EdgeInsets.only(left: 24, right: 24, top: 24, bottom: 12), child: - Semantics( header: true, hint: Localization().getStringEx("app.common.heading.one.hint","Header 1"), - child:Text(Localization().getStringEx('panel.health.onboarding.covid19.review_scan.label.title', 'Review your scan',), - textAlign: TextAlign.center, - style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary), - )) - ), - ), - ],),), - Expanded(child: - SingleChildScrollView(child: - Column(children: [ - _buildPreviewWidget(), - ],) - ), - ), - Container(height: 12,), - Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: - RoundedButton( - label: Localization().getStringEx('panel.health.onboarding.covid19.review_scan.button.rescan.title', 'Re-scan'), - hint: Localization().getStringEx('panel.health.onboarding.covid19.review_scan.button.rescan.hint', ''), - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - padding: EdgeInsets.symmetric(horizontal: 22), - onTap: () => _onRescan(), - height: 48, - ),), - Container(height: 12,), - Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: - Stack(children: [ - RoundedButton( - label: Localization().getStringEx('panel.health.onboarding.covid19.review_scan.button.use_scan.title', "Use This Scan"), - hint: Localization().getStringEx('panel.health.onboarding.covid19.review_scan.button.use_scan.hint', ''), - borderColor: Styles().colors.fillColorSecondary, - backgroundColor: Styles().colors.white, - textColor: Styles().colors.fillColorPrimary, - onTap: () => _onUseScan(), - height: 48, - ), - Visibility(visible: (_applyingScanResult == true), - child: Container(height: 48, - child: Align(alignment: Alignment.center, - child: SizedBox(height: 24, width: 24, - child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), ) - ), - ), - ), - ), - ],), - ), - Container(height: 24,), - ],),), - ); - } - - Widget _buildPreviewWidget() { - MemoryImage faceImage = (_scanResult != null) ? _scanResult[kFaceImageFieldName] : null; - - String nameText = ((_scanResult != null) ? _scanResult[kFullNameFieldName] : null) ?? ''; - String nameLabel = nameText.isNotEmpty ? Localization().getStringEx('panel.health.onboarding.covid19.review_scan.label.name.title', 'Name',) : ''; - - String birthYearText = ((_scanResult != null) ? _scanResult[kBirthYearFieldName] : null) ?? ''; - String birthYearLabel = birthYearText.isNotEmpty ? Localization().getStringEx('panel.health.onboarding.covid19.review_scan.label.birth_year.title', 'Birth Year',) : ''; - - return Padding(padding: EdgeInsets.symmetric(horizontal: 32, vertical: 32), - child: Container( - padding: EdgeInsets.all(16), - decoration: BoxDecoration( - color: Styles().colors.white, - borderRadius: BorderRadius.all(Radius.circular(4)), - boxShadow: [BoxShadow(color: Styles().colors.fillColorPrimaryTransparent015, spreadRadius: 2.0, blurRadius: 6.0, offset: Offset(0, 2))], - ), - child: Stack(children: [ - Visibility(visible: (_processingScanResult != true), child: - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container(width: 55, height: 70, - decoration: BoxDecoration( - color: Styles().colors.fillColorPrimaryTransparent03, - borderRadius: BorderRadius.all(Radius.circular(2)), - image: (faceImage != null) ? DecorationImage(fit: BoxFit.cover, alignment: Alignment.center, image: faceImage) : null, - ), - ), - Expanded( - child: Padding(padding: EdgeInsets.only(left: 16), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(nameLabel, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 16, color: Styles().colors.textSurface)), - Text(nameText, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary)), - Container(height: 16,), - Text(birthYearLabel, style: TextStyle(fontFamily: Styles().fontFamilies.regular, fontSize: 16, color: Styles().colors.textSurface)), - Text(birthYearText, style: TextStyle(fontFamily: Styles().fontFamilies.extraBold, fontSize: 20, color: Styles().colors.fillColorPrimary)), - ],), - ), - ), - ],), - ), - Visibility(visible: (_processingScanResult == true), - child: Container( - height: 70, - child: Align(alignment: Alignment.center, - child: SizedBox(height: 24, width: 24, - child: CircularProgressIndicator(strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Styles().colors.fillColorSecondary), ) - ), - ), - ), - ), - ],) - - - ), - ); - } - - static Map _buildScanResult(Map rawResult) { - - Map rawMrz = rawResult['mrz']; - - String rawFirstName = rawResult['firstName']; // "WILLIAM C III" - if ((rawFirstName == null) && (rawMrz != null)) { - rawFirstName = rawMrz['secondaryID']; // "PETER MARK" - } - String firstName = _buildName(rawFirstName); - String middleName = _buildName(rawFirstName, index: 1); - - String rawLastName = rawResult['lastName']; // "SULLIVAN" - if ((rawLastName == null) && (rawMrz != null)) { - rawLastName = rawMrz['primaryID']; // "HENNESSY" - } - String lastName = _buildName(rawLastName); - - String dateOfBirth = rawResult['dateOfBirth']; // "09/30/1958" - if ((dateOfBirth == null) && (rawMrz != null)) { - dateOfBirth = rawMrz['dateOfBirth']; // "11/22/1960" - } - String birthYear = ((dateOfBirth != null) && RegExp('[0-9]{2}/[0-9]{2}/[0-9]{4}').hasMatch(dateOfBirth)) ? dateOfBirth.substring(6, 10) : null; - - String country; - if (rawMrz != null) { - for (String key in ['sanitizedNationality', 'nationality', 'sanitizedIssuer', 'issuer']) { - String entry = rawMrz[key]; - if ((entry != null) && (0 < entry.length)) { - country = entry; - break; - } - } - } - - String rawAddress = rawResult['address']; // "1804 PLEASANT ST, URBANA, IL, 618010000" - String address = rawAddress, state, zip; - if (rawAddress != null) { - List addressComponents = rawAddress.split(','); - int componentsCount = addressComponents.length; - if ((addressComponents != null) && (1 < componentsCount)) { - - String aZip = addressComponents[componentsCount - 1].trim(); - bool hasZip = RegExp('[0-9]{5,}').hasMatch(aZip); - - String aState = addressComponents[componentsCount - 2].trim(); - bool hasState = RegExp('[a-zA-Z]{2,}').hasMatch(aState); - - if (hasZip && hasState) { - zip = aZip.substring(0, 5); - state = aState; - if (country == null) { - country = 'USA'; - } - - address = ''; - for (int index = 0; (index + 2) < componentsCount; index++) { - if (0 < index) { - address += ','; - } - address += addressComponents[index]; - } - } - } - } - - String fullName = ''; - if ((firstName != null) && (0 < firstName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$firstName"; - } - if ((middleName != null) && (0 < middleName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$middleName"; - } - if ((lastName != null) && (0 < lastName.length)) { - fullName += "${(0 < fullName.length) ? ' ' : ''}$lastName"; - } - - String fullAddress = address ?? ''; - if ((state != null) && (zip != null)) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$state $zip"; - } - else if (state != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$state"; - } - else if (zip != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$zip"; - } - if (country != null) { - fullAddress += "${(0 < fullAddress.length) ? ', ' : ''}$country"; - } - - String base64FaceImage = rawResult['base64FaceImage']; - Uint8List faceImageData = (base64FaceImage != null) ? base64Decode(base64FaceImage) : null; - MemoryImage faceImage = (faceImageData != null) ? MemoryImage(faceImageData) : null; - - return { - // These should go to PII - kFirstNameFieldName : firstName, - kMiddleNameFieldName : middleName, - kLastNameFieldName : lastName, - kBirthYearFieldName : birthYear, - kAddressFieldName : address, - kStateFieldName : state, - kZipFieldName : zip, - kCountryFieldName : country, - kFaceBase64FieldName : base64FaceImage, - - // These are for display purpose only - kFullNameFieldName : fullName, - kFullAddressFieldName : fullAddress, - kFaceImageFieldName : faceImage, - }; - } - - static String _buildName(String rawName, {int index = 0}) { - String resultName; - if (rawName != null) { - List firstNameComponents = rawName.split(' '); - if ((firstNameComponents != null) && (0 <= index) && (index < firstNameComponents.length)) { - resultName = firstNameComponents[index]; - } - else if (index == 0) { - resultName = rawName; - } - resultName = (resultName != null) ? AppString.capitalize(resultName) : null; - } - return resultName; - } - - Future _applyScan() async { - - UserPiiData updatedUserPiiData; - UserPiiData userPiiData = UserPiiData.fromObject(await Auth().reloadUserPiiData()); - if (userPiiData != null) { - _applyScanResult(userPiiData); - updatedUserPiiData = await Auth().storeUserPiiData(userPiiData); - } - - return (updatedUserPiiData != null); - } - - void _applyScanResult(UserPiiData userPiiData) { - - String photoBase64 = _scanResult[kFaceBase64FieldName]; - if (photoBase64 != null) { - userPiiData.photoBase64 = photoBase64; - } - - String firstName = _scanResult[kFirstNameFieldName]; - if (firstName != null) { - userPiiData.firstName = firstName; - } - - String middleName = _scanResult[kMiddleNameFieldName]; - if (middleName != null) { - userPiiData.middleName = middleName; - } - - String lastName = _scanResult[kLastNameFieldName]; - if (lastName != null) { - userPiiData.lastName = lastName; - } - - String birthYearString = _scanResult[kBirthYearFieldName]; - int birthYear = ((birthYearString != null) && (0 < birthYearString.length)) ? int.tryParse(birthYearString) : null; - if (birthYear != null) { - userPiiData.birthYear = birthYear; - } - - //Don't store this data in PiiData for now -/* String address = _scanResult[kAddressFieldName]; - if ((address != null) && (0 < address.length)) { - userPiiData.address = address; - } - - String state = _scanResult[kStateFieldName]; - if ((state != null) && (0 < state.length)) { - userPiiData.state = state; - } - - String zip = _scanResult[kZipFieldName]; - if ((zip != null) && (0 < zip.length)) { - userPiiData.zip = zip; - } - - String country = _scanResult[kCountryFieldName]; - if ((country != null) && (0 < country.length)) { - userPiiData.country = country; - }*/ - - if (_documenType != null) { - userPiiData.documentType = _documenType; - } -} - - void _goBack() { - Analytics.instance.logSelect(target: "Back"); - Navigator.of(context).pop(); - } - - void _goNext() { - if (Auth().isLoggedIn) { - widget.onboardingContext['shouldDisplayQrCode'] = true; - } else { - widget.onboardingContext['shouldDisplayQrCode'] = false; - } - Onboarding().next(context, widget); - } - - void _onRescan() { - Analytics.instance.logSelect(target: 'Re-scan') ; - - String analyticsScanType; - List recognizers; - if (_documenType == UserDocumentType.drivingLicense) { - analyticsScanType = Analytics.LogDocumentScanDrivingLicenseType; - recognizers = ['combined']; - } - else if (_documenType == UserDocumentType.passport) { - analyticsScanType = Analytics.LogDocumentScanPassportType; - recognizers = ['passport']; - } - - NativeCommunicator().microBlinkScan(recognizers: recognizers).then((dynamic result) { - Analytics().logDocumentScan(type: analyticsScanType, result: (result != null)); - if (result != null) { - _didRescan(result); - } - }); - } - - void _didRescan(Map scanData) { - setState(() { - _processingScanResult = true; - }); - compute(_buildScanResult, _scanData).then((Map scanResult) { - setState(() { - _processingScanResult = false; - _scanResult = scanResult; - }); - }); - - } - - void _onUseScan() { - Analytics.instance.logSelect(target: 'Use This Scan') ; - - if (_scanResult == null) { - return; - } - - setState(() { - _applyingScanResult = true; - }); - - _applyScan().then((bool result){ - - setState(() { - _applyingScanResult = false; - }); - - if (result) { - _goNext(); - } - else { - AppAlert.showDialogResult(context, Localization().getStringEx('panel.health.onboarding.covid19.review_scan.message.failed', 'Failed to apply scanned data',)); - } - }); - } - -} \ No newline at end of file From e0f6f29c156c28f146f1fe3edb8586aec50fe951 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 11:24:28 +0300 Subject: [PATCH 69/73] Removed microBlinkScan API from NativeCommunicator (#717). --- lib/service/NativeCommunicator.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/service/NativeCommunicator.dart b/lib/service/NativeCommunicator.dart index 69e49114..7616b154 100644 --- a/lib/service/NativeCommunicator.dart +++ b/lib/service/NativeCommunicator.dart @@ -142,15 +142,6 @@ class NativeCommunicator with Service { } } - Future microBlinkScan({List recognizers}) async { - try { - return await _platformChannel.invokeMethod('microBlinkScan', { 'recognizers' : recognizers }); - } on PlatformException catch (e) { - print(e.message); - } - return null; - } - Future> enabledOrientations(List orientationsList) async { List result; try { From 987ac6dda2d3619edbc5bee3575f721f5d38886e Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 11:38:26 +0300 Subject: [PATCH 70/73] Removed MicroBlink stuff from iOS (#717). --- ios/Podfile | 1 - ios/Runner.xcodeproj/project.pbxproj | 4 - ios/Runner/AppDelegate.m | 263 +-------------------------- 3 files changed, 1 insertion(+), 267 deletions(-) diff --git a/ios/Podfile b/ios/Podfile index 6fa5a192..07dff8c1 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -34,7 +34,6 @@ target 'Runner' do pod 'GoogleMaps', '3.3.0' pod 'ZXingObjC', '3.6.4' - pod 'PPBlinkID', '~> 5.3.0' pod 'HKDFKit', '0.0.3' # 'Firebase/MLVisionBarcodeModel' is required by 'firebase_ml_vision' plugin from pubspec.yaml. diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 48f3625a..fc4f09cf 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -621,7 +621,6 @@ "${BUILT_PRODUCTS_DIR}/HKDFKit/HKDFKit.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", - "${PODS_ROOT}/PPBlinkID/Microblink.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", @@ -667,7 +666,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HKDFKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Microblink.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", @@ -709,12 +707,10 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", - "${PODS_ROOT}/PPBlinkID/Microblink.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Microblink.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m index 3e24864b..5022f994 100644 --- a/ios/Runner/AppDelegate.m +++ b/ios/Runner/AppDelegate.m @@ -38,7 +38,6 @@ #import #import #import -#import #import #import @@ -59,7 +58,7 @@ @interface LaunchScreenView : UIView UIInterfaceOrientation _interfaceOrientationFromMask(UIInterfaceOrientationMask value); UIInterfaceOrientationMask _interfaceOrientationToMask(UIInterfaceOrientation value); -@interface AppDelegate() { +@interface AppDelegate() { } // Flutter @@ -74,13 +73,6 @@ @interface AppDelegate() Date: Wed, 29 Sep 2021 12:41:00 +0300 Subject: [PATCH 71/73] Android: remove MicroBlink sdk from the project [#717] --- android/app/build.gradle | 5 - android/app/src/main/AndroidManifest.xml | 5 - .../java/edu/illinois/covid/Constants.java | 1 - .../java/edu/illinois/covid/MainActivity.java | 271 ------------------ android/build.gradle | 1 - 5 files changed, 283 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index f2e00ad0..6e76aac2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -163,11 +163,6 @@ dependencies { implementation 'com.google.zxing:core:3.3.0' //Use zxing 3.3.0 because we have minSdk < 24 implementation ('com.journeyapps:zxing-android-embedded:4.1.0@aar') { transitive = false } - //BlinkID - implementation('com.microblink:blinkid:5.3.0@aar') { - transitive = true - } - // BLESSED - BLE library used for Exposures implementation 'com.github.weliem:blessed-android:1.19' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 62e2f961..6fb21f5d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -39,11 +39,6 @@ - - - - - diff --git a/android/app/src/main/java/edu/illinois/covid/Constants.java b/android/app/src/main/java/edu/illinois/covid/Constants.java index 0f07a429..6865be2a 100644 --- a/android/app/src/main/java/edu/illinois/covid/Constants.java +++ b/android/app/src/main/java/edu/illinois/covid/Constants.java @@ -32,7 +32,6 @@ public class Constants { static final String APP_DISMISS_SAFARI_VC_KEY = "dismissSafariVC"; static final String APP_DISMISS_LAUNCH_SCREEN_KEY = "dismissLaunchScreen"; static final String APP_ADD_CARD_TO_WALLET_KEY = "addToWallet"; - static final String APP_MICRO_BLINK_SCAN_KEY = "microBlinkScan"; static final String APP_ENABLED_ORIENTATIONS_KEY = "enabledOrientations"; static final String APP_NOTIFICATIONS_AUTHORIZATION = "notifications_authorization"; static final String APP_LOCATION_SERVICES_PERMISSION = "location_services_permission"; diff --git a/android/app/src/main/java/edu/illinois/covid/MainActivity.java b/android/app/src/main/java/edu/illinois/covid/MainActivity.java index d406eb0f..7337604c 100644 --- a/android/app/src/main/java/edu/illinois/covid/MainActivity.java +++ b/android/app/src/main/java/edu/illinois/covid/MainActivity.java @@ -17,7 +17,6 @@ package edu.illinois.covid; import android.Manifest; -import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.Intent; @@ -41,18 +40,6 @@ import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.journeyapps.barcodescanner.BarcodeEncoder; -import com.microblink.MicroblinkSDK; -import com.microblink.entities.recognizers.Recognizer; -import com.microblink.entities.recognizers.RecognizerBundle; -import com.microblink.entities.recognizers.blinkid.generic.BlinkIdCombinedRecognizer; -import com.microblink.entities.recognizers.blinkid.generic.DriverLicenseDetailedInfo; -import com.microblink.entities.recognizers.blinkid.mrtd.MrzResult; -import com.microblink.entities.recognizers.blinkid.passport.PassportRecognizer; -import com.microblink.intent.IntentDataTransferMode; -import com.microblink.recognition.InvalidLicenceKeyException; -import com.microblink.results.date.Date; -import com.microblink.uisettings.ActivityRunner; -import com.microblink.uisettings.BlinkIdUISettings; import java.io.ByteArrayOutputStream; import java.security.SecureRandom; @@ -62,7 +49,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Set; import java.util.UUID; @@ -97,15 +83,6 @@ public class MainActivity extends FlutterActivity implements MethodChannel.Metho private RequestLocationCallback rlCallback; - // BlinkId - private static final int BLINK_ID_REQUEST_CODE = 3; - private BlinkIdCombinedRecognizer blinkIdCombinedRecognizer; - private PassportRecognizer blinkIdPassportRecognizer; - private RecognizerBundle blinkIdRecognizerBundle; - private boolean scanning = false; - private boolean microblinkInitialized = false; - private MethodChannel.Result scanMethodChannelResult; - // Gallery Plugin private GalleryPlugin galleryPlugin; @@ -468,236 +445,6 @@ private String handleBarcode(Object params) { return barcodeImageData; } - //region BlinkId - - private void handleMicroBlinkScan(Object params) { - if (!microblinkInitialized) { - initMicroblinkSdk(); - } - if (!microblinkInitialized) { - Log.i(TAG, "Cannot start scanning! Microblink has not been initialized!"); - if (scanMethodChannelResult != null) { - scanMethodChannelResult.success(null); - } - return; - } - if (scanning) { - Log.d(TAG, "Blink Id is currently scanning!"); - if (scanMethodChannelResult != null) { - scanMethodChannelResult.success(null); - } - } else { - scanning = true; - List recognizersList = Arrays.asList("combined", "passport"); // by default - if (params instanceof HashMap) { - HashMap paramsMap = (HashMap) params; - Object recognizersObject = paramsMap.get("recognizers"); - if (recognizersObject instanceof List) { - recognizersList = (List) recognizersObject; - } - } - List recognizers = new ArrayList<>(); - for (String recognizerParam : recognizersList) { - if ("combined".equals(recognizerParam)) { - blinkIdCombinedRecognizer = new BlinkIdCombinedRecognizer(); - blinkIdCombinedRecognizer.setEncodeFaceImage(true); - recognizers.add(blinkIdCombinedRecognizer); - } else if ("passport".equals(recognizerParam)) { - blinkIdPassportRecognizer = new PassportRecognizer(); - blinkIdPassportRecognizer.setEncodeFaceImage(true); - recognizers.add(blinkIdPassportRecognizer); - } - } - blinkIdRecognizerBundle = new RecognizerBundle(recognizers); - BlinkIdUISettings uiSettings = new BlinkIdUISettings(blinkIdRecognizerBundle); - ActivityRunner.startActivityForResult(this, BLINK_ID_REQUEST_CODE, uiSettings); - } - } - - private void initMicroblinkSdk() { - String blinkIdLicenseKey = Utils.Map.getValueFromPath(keys, "microblink.blink_id.license_key", null); - if (Utils.Str.isEmpty(blinkIdLicenseKey)) { - Log.e(TAG, "Microblink BlinkId license key is missing from config keys!"); - return; - } - try { - MicroblinkSDK.setLicenseKey(blinkIdLicenseKey, this); - MicroblinkSDK.setIntentDataTransferMode(IntentDataTransferMode.PERSISTED_OPTIMISED); - microblinkInitialized = true; - } catch (InvalidLicenceKeyException | NullPointerException e) { - Log.e(TAG, "Microblink failed to initialize:"); - e.printStackTrace(); - } - } - - private void onBlinkIdScanSuccess(Intent data) { - Log.d(TAG, "onBlinkIdScanSuccess"); - if (blinkIdRecognizerBundle != null) { - blinkIdRecognizerBundle.loadFromIntent(data); - } - if ((blinkIdCombinedRecognizer != null) && (blinkIdCombinedRecognizer.getResult().getResultState() == Recognizer.Result.State.Valid)) { - onCombinedRecognizerResult(blinkIdCombinedRecognizer.getResult()); - } else if ((blinkIdPassportRecognizer != null) && (blinkIdPassportRecognizer.getResult().getResultState() == Recognizer.Result.State.Valid)) { - onPassportRecognizerResult(blinkIdPassportRecognizer.getResult()); - } - } - - private void onBlinkIdScanCanceled() { - Log.d(TAG, "onBlinkIdScanCanceled"); - unInitBlinkId(); - if (scanMethodChannelResult != null) { - scanMethodChannelResult.success(null); - } - } - - private void onCombinedRecognizerResult(BlinkIdCombinedRecognizer.Result combinedRecognizerResult) { - Log.d(TAG, "onCombinedRecognizerResult"); - HashMap scanResult = null; - if (combinedRecognizerResult != null) { - scanResult = new HashMap<>(); - - String base64FaceImage = Base64.encodeToString(combinedRecognizerResult.getEncodedFaceImage(), Base64.NO_WRAP); - - scanResult.put("firstName", Utils.Str.nullIfEmpty(combinedRecognizerResult.getFirstName())); - scanResult.put("lastName", Utils.Str.nullIfEmpty(combinedRecognizerResult.getLastName())); - scanResult.put("fullName", Utils.Str.nullIfEmpty(combinedRecognizerResult.getFullName())); - scanResult.put("sex", Utils.Str.nullIfEmpty(combinedRecognizerResult.getSex())); - scanResult.put("address", Utils.Str.nullIfEmpty(combinedRecognizerResult.getAddress())); - - scanResult.put("dateOfBirth", Utils.Str.nullIfEmpty(formatBlinkIdDate(combinedRecognizerResult.getDateOfBirth().getDate()))); - scanResult.put("dateOfExpiry", Utils.Str.nullIfEmpty(formatBlinkIdDate(combinedRecognizerResult.getDateOfExpiry().getDate()))); - scanResult.put("dateOfIssue", Utils.Str.nullIfEmpty(formatBlinkIdDate(combinedRecognizerResult.getDateOfIssue().getDate()))); - - scanResult.put("documentNumber", Utils.Str.nullIfEmpty(combinedRecognizerResult.getDocumentNumber())); - - scanResult.put("placeOfBirth", Utils.Str.nullIfEmpty(combinedRecognizerResult.getPlaceOfBirth())); - scanResult.put("nationality", Utils.Str.nullIfEmpty(combinedRecognizerResult.getNationality())); - scanResult.put("race", Utils.Str.nullIfEmpty(combinedRecognizerResult.getRace())); - scanResult.put("religion", Utils.Str.nullIfEmpty(combinedRecognizerResult.getReligion())); - scanResult.put("profession", Utils.Str.nullIfEmpty(combinedRecognizerResult.getProfession())); - scanResult.put("maritalStatus", Utils.Str.nullIfEmpty(combinedRecognizerResult.getMaritalStatus())); - scanResult.put("residentialStatus", Utils.Str.nullIfEmpty(combinedRecognizerResult.getResidentialStatus())); - scanResult.put("employer", Utils.Str.nullIfEmpty(combinedRecognizerResult.getEmployer())); - scanResult.put("personalIdNumber", Utils.Str.nullIfEmpty(combinedRecognizerResult.getPersonalIdNumber())); - scanResult.put("documentAdditionalNumber", Utils.Str.nullIfEmpty(combinedRecognizerResult.getDocumentAdditionalNumber())); - scanResult.put("issuingAuthority", Utils.Str.nullIfEmpty(combinedRecognizerResult.getIssuingAuthority())); - - scanResult.put("mrz", getScanRezultFromMrz(combinedRecognizerResult.getMrzResult())); - scanResult.put("driverLicenseDetailedInfo", getScanResultFromDriverLicenseDetailedInfo(combinedRecognizerResult.getDriverLicenseDetailedInfo())); - - scanResult.put("base64FaceImage", Utils.Str.nullIfEmpty(base64FaceImage)); - } - unInitBlinkId(); - if (scanMethodChannelResult != null) { - scanMethodChannelResult.success(scanResult); - } - } - - private void onPassportRecognizerResult(PassportRecognizer.Result passportRecognizerResult) { - Log.d(TAG, "onPassportRecognizerResult"); - HashMap scanResult = null; - if (passportRecognizerResult != null) { - scanResult = new HashMap<>(); - - String base64FaceImage = Base64.encodeToString(passportRecognizerResult.getEncodedFaceImage(), Base64.NO_WRAP); - - scanResult.put("firstName", null); - scanResult.put("lastName", null); - scanResult.put("fullName", null); - scanResult.put("sex", null); - scanResult.put("address", null); - - scanResult.put("dateOfBirth", null); - scanResult.put("dateOfExpiry", null); - scanResult.put("dateOfIssue", null); - - scanResult.put("documentNumber", null); - - scanResult.put("placeOfBirth", null); - scanResult.put("nationality", null); - scanResult.put("race", null); - scanResult.put("religion", null); - scanResult.put("profession", null); - scanResult.put("maritalStatus", null); - scanResult.put("residentialStatus", null); - scanResult.put("employer", null); - scanResult.put("personalIdNumber", null); - scanResult.put("documentAdditionalNumber", null); - scanResult.put("issuingAuthority", null); - - scanResult.put("mrz", getScanRezultFromMrz(passportRecognizerResult.getMrzResult())); - scanResult.put("driverLicenseDetailedInfo", null); - - scanResult.put("base64FaceImage", Utils.Str.nullIfEmpty(base64FaceImage)); - } - unInitBlinkId(); - if (scanMethodChannelResult != null) { - scanMethodChannelResult.success(scanResult); - } - } - - private HashMap getScanResultFromDriverLicenseDetailedInfo(DriverLicenseDetailedInfo driverLicenseDetailedInfo) { - HashMap scanResult = null; - if (driverLicenseDetailedInfo != null) { - scanResult = new HashMap<>(); - - scanResult.put("restrictions", Utils.Str.nullIfEmpty(driverLicenseDetailedInfo.getRestrictions())); - scanResult.put("endorsements", Utils.Str.nullIfEmpty(driverLicenseDetailedInfo.getEndorsements())); - scanResult.put("vehicleClass", Utils.Str.nullIfEmpty(driverLicenseDetailedInfo.getVehicleClass())); - } - return scanResult; - } - - private HashMap getScanRezultFromMrz(MrzResult mrzRezult) { - HashMap scanResult = null; - if (mrzRezult != null) { - scanResult = new HashMap<>(); - - scanResult.put("primaryID", Utils.Str.nullIfEmpty(mrzRezult.getPrimaryId())); - scanResult.put("secondaryID", Utils.Str.nullIfEmpty(mrzRezult.getSecondaryId())); - scanResult.put("issuer", Utils.Str.nullIfEmpty(mrzRezult.getIssuer())); - scanResult.put("issuerName", Utils.Str.nullIfEmpty(mrzRezult.getIssuerName())); - scanResult.put("dateOfBirth", Utils.Str.nullIfEmpty(formatBlinkIdDate(mrzRezult.getDateOfBirth().getDate()))); - scanResult.put("dateOfExpiry", Utils.Str.nullIfEmpty(formatBlinkIdDate(mrzRezult.getDateOfExpiry().getDate()))); - scanResult.put("documentNumber", Utils.Str.nullIfEmpty(mrzRezult.getDocumentNumber())); - scanResult.put("nationality", Utils.Str.nullIfEmpty(mrzRezult.getNationality())); - scanResult.put("nationalityName", Utils.Str.nullIfEmpty(mrzRezult.getNationalityName())); - scanResult.put("gender", Utils.Str.nullIfEmpty(mrzRezult.getGender())); - scanResult.put("documentCode", Utils.Str.nullIfEmpty(mrzRezult.getDocumentCode())); - scanResult.put("alienNumber", Utils.Str.nullIfEmpty(mrzRezult.getAlienNumber())); - scanResult.put("applicationReceiptNumber", Utils.Str.nullIfEmpty(mrzRezult.getApplicationReceiptNumber())); - scanResult.put("immigrantCaseNumber", Utils.Str.nullIfEmpty(mrzRezult.getImmigrantCaseNumber())); - - scanResult.put("opt1", Utils.Str.nullIfEmpty(mrzRezult.getOpt1())); - scanResult.put("opt2", Utils.Str.nullIfEmpty(mrzRezult.getOpt2())); - scanResult.put("mrzText", Utils.Str.nullIfEmpty(mrzRezult.getMrzText())); - - scanResult.put("sanitizedOpt1", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedOpt1())); - scanResult.put("sanitizedOpt2", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedOpt2())); - scanResult.put("sanitizedNationality", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedNationality())); - scanResult.put("sanitizedIssuer", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedIssuer())); - scanResult.put("sanitizedDocumentCode", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedDocumentCode())); - scanResult.put("sanitizedDocumentNumber", Utils.Str.nullIfEmpty(mrzRezult.getSanitizedDocumentNumber())); - } - return scanResult; - } - - private void unInitBlinkId() { - blinkIdRecognizerBundle = null; - blinkIdCombinedRecognizer = null; - blinkIdPassportRecognizer = null; - scanning = false; - } - - private String formatBlinkIdDate(Date date) { - if (date == null) { - return null; - } - return String.format(Locale.getDefault(), "%02d/%02d/%4d", date.getMonth(), date.getDay(), date.getYear()); - } - - //endregion - //region Health RSA keys private Object handleHealthRsaPrivateKey(Object params) { @@ -797,19 +544,6 @@ private Object handleEncryptionKey(Object params) { //endregion - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == BLINK_ID_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - onBlinkIdScanSuccess(data); - } else { - onBlinkIdScanCanceled(); - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - /** * Overrides {@link io.flutter.plugin.common.MethodChannel.MethodCallHandler} onMethodCall() */ @@ -846,11 +580,6 @@ public void onMethodCall(MethodCall methodCall, @NonNull MethodChannel.Result re case Constants.APP_ADD_CARD_TO_WALLET_KEY: result.success(false); break; - case Constants.APP_MICRO_BLINK_SCAN_KEY: - scanMethodChannelResult = result; - handleMicroBlinkScan(methodCall.arguments); - // Result is called on latter step - break; case Constants.APP_ENABLED_ORIENTATIONS_KEY: Object orientations = methodCall.argument("orientations"); List orientationsList = handleEnabledOrientations(orientations); diff --git a/android/build.gradle b/android/build.gradle index 72a3775c..96d73503 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -38,7 +38,6 @@ allprojects { flatDir { dirs '../lib' } - maven { url 'https://maven.microblink.com' } maven { url 'https://jitpack.io' } } } From 3d0f584f1a66af83940142c26eac536f334daa67 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 12:46:44 +0300 Subject: [PATCH 72/73] Updated CHANGELOG.md (#717). --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4040efa3..c9c46ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Deleted - Removed vaccine taken event handling [#715](https://github.com/rokwire/safer-illinois-app/issues/715). +- Removed MicroBlink scan support [#717](https://github.com/rokwire/safer-illinois-app/issues/717). ## [2.11.2] - 2021-09-24 ### Changed From 0838583974dcfa6145cf999ad41595b7850a4ff5 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Wed, 29 Sep 2021 13:17:43 +0300 Subject: [PATCH 73/73] version: 2.11.3+1103 --- CHANGELOG.md | 2 ++ pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c46ffc..caf6606f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased + +## [2.11.3] - 2021-09-29 ### Deleted - Removed vaccine taken event handling [#715](https://github.com/rokwire/safer-illinois-app/issues/715). - Removed MicroBlink scan support [#717](https://github.com/rokwire/safer-illinois-app/issues/717). diff --git a/pubspec.yaml b/pubspec.yaml index cd016122..b00799c6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.11.2+1102 +version: 2.11.3+1103 environment: sdk: ">=2.2.0 <3.0.0"