diff --git a/.circleci/config.yml b/.circleci/config.yml index 20f3f4a0c66333127c4e0480a8e3b4a3daca7be0..249d3793e1021f1295507f12fa62b65ef17a28db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -493,47 +493,6 @@ jobs: - store_artifacts: path: /tmp/instrumentation_tests_device.zip destination: zips/instrumentation_tests_device.zip - # Keep it until Firebase Test Lab proves reliability - device_screenshots: - macos: - xcode: "12.3.0" - resource_class: large - steps: - - checkout - - restore_cache: - key: gem-cache-v1-{{ arch }}-{{ checksum "Gemfile.lock" }} - - run: bundle check || bundle install --path vendor/bundle - - save_cache: - key: gem-cache-v1-{{ arch }}-{{ checksum "Gemfile.lock" }} - paths: - - vendor/bundle - - setup-android-macos - - restore-gradle-cache - - restore-android-build-cache-macos - - run-gradle-cmd: - desc: Build APKs for screenshots - cmd: > - :Corona-Warn-App:assembleDebug - :Corona-Warn-App:assembleAndroidTest - - save-gradle-cache - - save-android-build-cache-macos - - run-emulator-for-api: - apilevel: 29 - - run: - name: Run fastlane screengrab - command: | - for i in {1..5}; do bundle exec fastlane screengrab && break || sleep 15; done - no_output_timeout: 30m - - kill-all-emulators - - store_artifacts: - path: fastlane/metadata/android - destination: screenshots - - compress-path: - input: ./fastlane/metadata/android - output: /tmp/device_screenshots.zip - - store_artifacts: - path: /tmp/device_screenshots.zip - destination: zips/device_screenshots.zip firebase_screenshots: resource_class: xlarge diff --git a/.reuse/dep5 b/.reuse/dep5 index ae4ac319fb88ee2f0c1dc565ae79e4385f673f2d..25db19164e49c4b5d1f4f7853eb9c16a4310438b 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -54,4 +54,8 @@ License: Apache-2.0 Files: Corona-Warn-App/src/main/res/font/roboto.ttf Copyright: 2011 Google Inc. +License: Apache-2.0 + +Files: Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt +Copyright: 2019 The Android Open Source Project License: Apache-2.0 \ No newline at end of file diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt index 1fa7e54d4fa91340b2b93f0cd7e5cc449a1811a8..3cf44a65b58741a7f91b905ec23c5ab1cf6537d2 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.test.contactdiary.ui import android.annotation.SuppressLint import android.os.Bundle import android.view.View +import androidx.core.widget.TextViewCompat import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentTestContactDiaryBinding @@ -84,10 +85,10 @@ class ContactDiaryTestFragment : text = duration.toContactDiaryFormat() if (duration.millis == 0L) { setBackgroundResource(R.drawable.contact_diary_duration_background_default) - setTextAppearance(R.style.bodyNeutral) + TextViewCompat.setTextAppearance(this, R.style.bodyNeutral) } else { setBackgroundResource(R.drawable.contact_diary_duration_background_selected) - setTextAppearance(R.style.body1) + TextViewCompat.setTextAppearance(this, R.style.body1) } } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt index 638755b2c4ddae1e3f0355209ccd4f635023816a..b0e02bfce947e3a0776b4ed9eca95af2ca08d524 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt @@ -75,7 +75,7 @@ class DebugOptionsFragment : Fragment(R.layout.fragment_test_debugoptions), Auto environmentPubkeyAppconfig.text = "AppConfigPubKey:\n${state.pubKeyAppConfig}" } } - vm.environmentChangeEvent.observe2(this) { + vm.environmentStateChange.observe2(this) { showSnackBar("Environment changed to: $it\nForce stop & restart the app!") } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt index 9d959a1db3d704e446d3261244ba1afe18c5a99d..f7f5f5d487f43b63c17e601105eaf972db722621 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.test.debugoptions.ui +import androidx.lifecycle.asLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.environment.EnvironmentSetup @@ -7,25 +8,24 @@ import de.rki.coronawarnapp.environment.EnvironmentSetup.Type.Companion.toEnviro import de.rki.coronawarnapp.test.debugoptions.ui.EnvironmentState.Companion.toEnvironmentState import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.ui.smartLiveData import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.MutableStateFlow class DebugOptionsFragmentViewModel @AssistedInject constructor( private val envSetup: EnvironmentSetup, dispatcherProvider: DispatcherProvider ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val environmentState by smartLiveData { - envSetup.toEnvironmentState() - } - val environmentChangeEvent = SingleLiveEvent<EnvironmentSetup.Type>() + private val environmentStateFlow = MutableStateFlow(envSetup.toEnvironmentState()) + val environmentState = environmentStateFlow.asLiveData(context = dispatcherProvider.Default) + val environmentStateChange = SingleLiveEvent<EnvironmentState>() fun selectEnvironmentTytpe(type: String) { - environmentState.update { - envSetup.currentEnvironment = type.toEnvironmentType() - environmentChangeEvent.postValue(envSetup.currentEnvironment) - envSetup.toEnvironmentState() + envSetup.currentEnvironment = type.toEnvironmentType() + envSetup.toEnvironmentState().let { + environmentStateFlow.value = it + environmentStateChange.postValue(it) } } diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml index 2fc4b03cbcd62f7c3ddd11e4f7a3d0ebab02cf5d..20dd21fbdf14406fbba6e0a6a2a388c57b8b3c5a 100644 --- a/Corona-Warn-App/src/main/AndroidManifest.xml +++ b/Corona-Warn-App/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ package="de.rki.coronawarnapp" tools:ignore="LockedOrientationActivity"> - <uses-sdk tools:overrideLibrary="com.google.zxing.client.android" /> + <uses-sdk tools:overrideLibrary="com.google.zxing.client.android,androidx.security" /> <uses-feature android:name="android.hardware.bluetooth_le" diff --git a/Corona-Warn-App/src/main/assets/privacy_de.html b/Corona-Warn-App/src/main/assets/privacy_de.html index 4e1f055fe0b4fd91d0a28c44df4638e46dcce848..138ff960889a9eac51814eecc6b1e9ca9646907b 100644 --- a/Corona-Warn-App/src/main/assets/privacy_de.html +++ b/Corona-Warn-App/src/main/assets/privacy_de.html @@ -77,7 +77,8 @@ <p> Die Nutzung der App ist freiwillig. Es ist allein Ihre Entscheidung, ob Sie die App installieren, welche App-Funktionen Sie nutzen und ob Sie Daten mit anderen teilen. Alle - App-Funktionen, die eine Weitergabe Ihrer personenbezogenen Daten an das RKI oder an andere + Haupt-Funktionen der App, die eine Weitergabe Ihrer personenbezogenen Daten an das RKI oder an + andere Nutzer erfordern, holen vorher Ihr ausdrückliches Einverständnis ein. Falls Sie ein Einverständnis nicht erteilen oder nachträglich zurücknehmen, entstehen Ihnen keine Nachteile. </p> @@ -90,7 +91,7 @@ Falle von Gesundheitsdaten Art. 9 Abs. 2 lit. a DSGVO. Sie können ein einmal erteiltes Einverständnis jederzeit wieder zurücknehmen (sogenanntes Widerrufsrecht). Weitere Informationen zu Ihrem Widerrufsrecht finden Sie unter Punkt 12. Die Verarbeitung von Zugriffsdaten für die - Bereitstellung der täglichen Statistiken (siehe hierzu Punkt 6.e.) erfolgt im Rahmen der + Bereitstellung der täglichen Statistiken (siehe hierzu Punkt 6 f.) erfolgt im Rahmen der Information der Öffentlichkeit durch das RKI gem. § 4 Abs. 4 BGA-NachfG auf Basis von Art. 6 Abs. 1 lit e. DSGVO i.V.m § 3 BDSG. </p> @@ -109,14 +110,16 @@ verarbeitet werden. Das bedeutet, dass das System bei der Risiko-Ermittlung, der Warnung anderer und dem Abruf des Testergebnisses keine Daten erfassen muss, die es dem RKI oder anderen Nutzern ermöglichen, auf Ihre Identität, Ihren Namen, Ihren Standort oder andere persönliche Details zu - schließen. Eine Ausnahme gilt nur für die optionale Funktion „Schnelltest-Ergebnis nachweisen“, - mit der Sie eine auf Ihren Namen ausgestellte Bestätigung für negative Schnelltest-Ergebnisse - anzeigen lassen können (siehe hierzu Punkt 6 c.) + schließen. Eine Ausnahme gilt nur für die Funktion „Schnelltest-Ergebnis nachweisen“, mit der + Sie eine auf Ihren Namen ausgestellte Bestätigung für negative Schnelltest-Ergebnisse anzeigen + lassen können (siehe hierzu Punkt 6 c.)sowie die Funktion zum Anlegen eines + „Schnelltest-Profils“, mit der Sie einer Teststelle die zur Durchführung eines Schnelltests + erforderlichen Daten bereitstellen können (siehe hierzu Punkt 6 d.). </p> <p> - Die App verzichtet zudem standardmäßig auf die Auswertung Ihres Nutzungsverhaltens durch + Die App verzichtet standardmäßig auf die Auswertung Ihres Nutzungsverhaltens durch Analyse-Tools. Nur wenn Sie ausdrücklich der freiwilligen Datenspende zustimmen, werden - bestimmte Daten über Ihre Nutzung der App an das RKI übermittelt (siehe hierzu Punkt 5 g.). + bestimmte Daten über Ihre Nutzung der App an das RKI übermittelt (siehe hierzu Punkt 5 h). </p> <p> Die von der App verarbeiteten Daten lassen sich den folgenden Kategorien @@ -243,7 +246,20 @@ QR-Code in kodierter Form weitere von Ihnen angegebene Daten. </p> <h2> - d. Event-Daten + d. Schnelltest-Profil +</h2> +<p> + Sie können Angaben zu Ihrer Person in Ihrem Schnelltest-Profil in der App speichern. Das + Schnelltest-Profil umfasst folgende Felder: Vorname, Nachname, Geburtsdatum, Straße und + Hausnummer, PLZ, Stadt, Telefonnummer, E-Mail-Adresse. Die App wandelt Ihre Daten dann in Ihren + persönlichen QR-Code um, der alle von Ihnen eingegebenen Daten enthält. Das Anlegen eines + Schnelltest-Profils in der App und dessen Verwendung in einer Teststelle sind freiwillig. Sie + entscheiden selbst, welche Daten Sie in Ihrem Schnelltest-Profil eintragen. Nur diese Daten sind + im QR-Code enthalten. Sollte die Teststelle weitere Angaben benötigen, die nicht im QR-Code + enthalten sind, können Sie diese der Teststelle auch auf andere Weise mitteilen. +</p> +<h2> + e. Event-Daten </h2> <p> Wenn Sie ein Event (beispielsweise eine Feier oder ein Konzert) oder einen Ort (beispielsweise @@ -270,7 +286,7 @@ </p> <p> Außerdem wird standardmäßig ein Eintrag in Ihrem Kontakt-Tagebuch angelegt. Die Einzelheiten - hierzu werden unter Punkt 5.e. und 6.e. erläutert. Wenn Sie für ein Event oder Ort keinen + hierzu werden unter Punkt 5 g. und 6 g. erläutert. Wenn Sie für ein Event oder Ort keinen Eintrag in Ihrem Kontakt-Tagebuch anlegen möchten, können Sie diese Funktion einfach über den entsprechenden Schieberegler ausschalten. </p> @@ -292,7 +308,7 @@ einchecken. </p> <h2> - e. Gesundheitsdaten + f. Gesundheitsdaten </h2> <p> Gesundheitsdaten sind alle Daten, die Informationen zum Gesundheitszustand @@ -320,7 +336,7 @@ Die Einzelheiten werden unter Punkt 6 erläutert. </p> <h2> - f. Einträge im Kontakt-Tagebuch + g. Einträge im Kontakt-Tagebuch </h2> <p> Wenn Sie im Kontakt-Tagebuch notieren, wann und wo Sie welche Personen getroffen haben und @@ -342,7 +358,7 @@ Verfügung stellen können. </p> <h2> - g. Nutzungsdaten (Datenspende) + h. Nutzungsdaten (Datenspende) </h2> <p> Wenn Sie die Datenspende aktivieren, übermittelt die App bestimmte Daten über Ihre App-Nutzung @@ -409,10 +425,10 @@ <p> Die Teilnahme an der Datenspende ist freiwillig. Die Aktivierung der Datenspende setzt die Bestätigung der Echtheit Ihrer App voraus (Beachten Sie bitte die weiteren Informationen hierzu - unter Punkt 5 i. und Punkt 11). + unter Punkt 5 j. und Punkt 11). </p> <h2> - h. Teilnahme an einer Befragung + i. Teilnahme an einer Befragung </h2> <p> Einigen Nutzern wird in der App die Teilnahme an einer Befragung des RKI angeboten. In der Regel @@ -427,10 +443,10 @@ Befragung teilnehmen möchten und Daten hierfür an das RKI übermittelt werden sollen. Die Befragungen finden auf einer Webseite außerhalb der App statt, auf die Sie weitergeleitet werden. Die Teilnahme an einer Befragung setzt die Bestätigung der Echtheit Ihrer App voraus - (Beachten Sie bitte die weitere Informationen hierzu unter Punkt 5 i. und Punkt 11). + (Beachten Sie bitte die weitere Informationen hierzu unter Punkt 5 j. und Punkt 11). </p> <h2> - i. Bestätigung der Echtheit Ihrer App + j. Bestätigung der Echtheit Ihrer App </h2> <p> Einige Funktionen der App setzen voraus, dass vorab die Echtheit Ihrer App geprüft und gegenüber @@ -464,15 +480,15 @@ </p> <p> Hierzu ruft die App vom Serversystem mehrmals täglich eine aktuelle Positiv-Liste mit den - Angaben von Nutzern, die über die offizielle Corona-App eines am länderübergreifenden Warnsystem - teilnehmenden Landes (siehe hierzu Punkt 7) eine Warnung ausgelöst haben ab. Diese Positiv-Liste - enthält die Zufalls-IDs der warnenden Nutzer sowie eventuelle Angaben zum Symptombeginn. Falls - die warnenden Nutzer bei einem Event eingecheckt waren, enthält die Positiv-Liste auch die - betreffenden Event-IDs und die Dauer des Check-ins (Eincheck- und Auscheck-Zeit). + Angaben von Nutzern ab, die über eine offizielle Corona-App eine Warnung ausgelöst haben (siehe + hierzu Punkt 7). Diese Positiv-Liste enthält die Zufalls-IDs der warnenden Nutzer sowie + eventuelle Angaben zum Symptombeginn. Falls die warnenden Nutzer bei einem Event eingecheckt + waren, enthält die Positiv-Liste auch die betreffenden Event-IDs und die Dauer des Check-ins + (Eincheck- und Auscheck-Zeit). </p> <p> Die Zufalls-IDs und Event-IDs in den Positiv-Listen enthalten zusätzlich einen - Ãœbertragungsrisiko-Wert und eine Angabe zur Art der Diagnose (siehe hierzu Punkt 6 d.). + Ãœbertragungsrisiko-Wert und eine Angabe zur Art der Diagnose (siehe hierzu Punkt 6 e.). </p> <p> Die App gibt die Zufalls-IDs aus der Positiv-Liste an das COVID-19-Benachrichtigungssystem @@ -613,7 +629,22 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende Schnelltest-Ergebnis nicht mehr in der App angezeigt wird. </p> <h2> - d. Andere warnen + d. Schnelltest-Profil +</h2> +<p> + Das Schnelltest-Profil bietet Ihnen die Möglichkeit, die Datenerfassung in teilnehmenden + Teststellen zu beschleunigen. Sie können hierfür Angaben zu Ihrer Person in Ihrem + Schnelltest-Profil in der App speichern und diese in Ihren persönlichen QR-Code umwandeln, der + alle eingegebenen Daten enthält. In der Teststelle können Sie den QR-Code Ihres + Schnelltest-Profils in Ihrer App vorzeigen, damit dieser von der Teststelle gescannt und die von + Ihnen angegebenen Daten auslesen werden können. Die für die Durchführung des Schnelltests + erforderlichen Daten werden so schnell und sicher bereitgestellt. Sie entscheiden selbst, welche + Daten Sie im Schnelltest-Profil angeben und ob Sie dieses in Teststellen vorzeigen. Angaben, die + die Teststelle benötigt und die nicht im QR-Code enthalten sind, können Sie der Teststelle auch + auf andere Weise mitteilen. +</p> +<h2> + e. Andere warnen </h2> <p> Wenn Sie Corona-positiv getestet sind und Ihre Zufalls-IDs mit der App teilen, können andere @@ -690,7 +721,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende außer mit Ihnen keine anderen Kontakte hatte. </p> <h2> - e. Informatorische Nutzung der App + f. Informatorische Nutzung der App </h2> <p> Die täglichen Statistiken, die in der App erscheinen, erhält die App @@ -701,7 +732,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende jeweiligen Anbietern der aufgerufenen Webseite festgelegt. </p> <h2> - f. Kontakt-Tagebuch + g. Kontakt-Tagebuch </h2> <p> Das Kontakt-Tagebuch ist eine Zusatzfunktion der App. Ihre Einträge im Kontakt-Tagebuch dienen @@ -716,7 +747,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende Ansteckungen verhindern. </p> <h2> - g. Datenspende + h. Datenspende </h2> <p> Die Datenspende ist eine Zusatzfunktionen der App. Die im Rahmen der Datenspende an das RKI @@ -757,7 +788,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende oder wen Sie getroffen haben. </p> <h2> - h. Befragungen + i. Befragungen </h2> <p> Befragungen finden auf einer Webseite außerhalb der App statt, auf die Sie weitergeleitet @@ -766,7 +797,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende Befragung auf der Befragungs-Webseite beschrieben. </p> <h2> - i. Bestätigung der Echtheit Ihrer App + j. Bestätigung der Echtheit Ihrer App </h2> <p> Zur Bestätigung der Echtheit Ihrer App wird eine Funktion des Betriebssystems Ihres Smartphones @@ -812,7 +843,7 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende <p> Das jeweilige Serversystem führt die erhaltenen Positiv-Listen mit der eigenen Positiv-Liste zusammen, so dass die Risiko-Ermittlung auch Risiko-Begegnungen mit Nutzern einer anderen - Corona-App berücksichtigen kann (siehe Ziffer 6 d.). Die anderen teilnehmenden Länder verfahren + Corona-App berücksichtigen kann (siehe Punkt 6 e.). Die anderen teilnehmenden Länder verfahren entsprechend mit den vom RKI bereitgestellten Positiv-Listen. </p> <p> @@ -966,7 +997,11 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende gelöscht. Sofern Sie ein positives Testergebnis abgerufen haben, wird das Token im App-Speicher gelöscht, sobald Sie eine Warnung auslösen. Die Einträge im Kontakt-Tagebuch bleiben für 16 Tage auf Ihrem Smartphone gespeichert und werden dann - automatisch gelöscht. Sie können diese Einträge jederzeit auch vorzeitig selbst löschen. + automatisch gelöscht. Sie können diese Einträge jederzeit auch vorzeitig selbst löschen. Bitte + beachten Sie, dass beim Einchecken zu einem Event oder an einem Ort übernommene Einträge im + Kontakt-Tagebuch auch nach der Löschung des zugehörigen Check-Ins dort noch gespeichert sind. + Wenn Sie Ihr Schnelltest-Profil einmal angelegt haben, bleibt dieses so lange in der App + gespeichert, bis Sie es selbst wieder löschen. </p> <h2> b. Daten auf Serversystemen @@ -1013,18 +1048,20 @@ Das für die letzten 14 Tage jeweils berechnete Risiko wird Ihnen in der Kalende Schweizerischen Eidgenossenschaft in Abstimmung mit dem RKI betrieben und gewartet. </p> <p> - Mit dem Betrieb und der Wartung eines Teils der technischen Infrastruktur - der App (z. B. Serversysteme, Hotline) hat das RKI die T-Systems - International GmbH und die SAP Deutschland SE & Co. KG beauftragt, die - als Auftragsverarbeiter des RKI tätig werden. Diese Unternehmen sind von - der EU-Kommission zudem als Unterauftragsverarbeiter mit der technischen - Bereitstellung und Verwaltung des gemeinsam betriebenen Warnsystems der - teilnehmenden Länder beauftragt. Im Ãœbrigen gibt das RKI Ihre Daten, die im Zusammenhang mit der Nutzung der - App erhoben werden, nur an Dritte weiter, soweit das RKI rechtlich dazu - verpflichtet ist oder die Weitergabe im Falle von Angriffen auf die - technische Infrastruktur der App zur Rechts- oder Strafverfolgung - erforderlich ist. Eine Weitergabe durch das RKI in anderen Fällen erfolgt grundsätzlich - nicht. + Mit dem Betrieb und der Wartung des gemeinsam betriebenen Austausch-Servers der teilnehmenden + EU-Länder haben die jeweils zuständigen nationalen Gesundheitsbehörden die EU-Kommission als + Auftragsverarbeiter beauftragt. Der Austausch-Server für länderübergreifende Warnungen zwischen + der Corona-Warn-App und der schweizerischen Corona-App wird vom Bundesamt für Gesundheit der + Schweizerischen Eidgenossenschaft in Abstimmung mit dem RKI betrieben und gewartet. Mit dem + Betrieb und der Wartung eines Teils der technischen Infrastruktur der App (z. B. Serversysteme, + Hotline) hat das RKI die T-Systems International GmbH und die SAP Deutschland SE & Co. KG + beauftragt, die als Auftragsverarbeiter des RKI tätig werden. Diese Unternehmen sind von der + EU-Kommission zudem als Unterauftragsverarbeiter mit der technischen Bereitstellung und + Verwaltung des gemeinsam betriebenen Warnsystems der teilnehmenden Länder beauftragt. Im Ãœbrigen + gibt das RKI Ihre Daten, die im Zusammenhang mit der Nutzung der App erhoben werden, nur an + Dritte weiter, soweit das RKI rechtlich dazu verpflichtet ist oder die Weitergabe im Falle von + Angriffen auf die technische Infrastruktur der App zur Rechts- oder Strafverfolgung erforderlich + ist. Eine Weitergabe durch das RKI in anderen Fällen erfolgt grundsätzlich nicht. </p> <h1> 11. Werden Ihre Daten in Länder außerhalb der EU übermittelt? diff --git a/Corona-Warn-App/src/main/assets/privacy_en.html b/Corona-Warn-App/src/main/assets/privacy_en.html index 0f8b3bcd162ba987e8325087ca0f9a445d21eec1..0e548ea78d3c38ccc440408ebb5a7233973658ae 100644 --- a/Corona-Warn-App/src/main/assets/privacy_en.html +++ b/Corona-Warn-App/src/main/assets/privacy_en.html @@ -76,7 +76,8 @@ </h1> <p> Using the app is voluntary. It is entirely up to you whether you install the app, which of the - app’s features you use, and whether you share data with others. All of the app’s features that + app’s features you use, and whether you share data with others. All of the app’s main features + that require the transfer of your personal data to the RKI or to other users will obtain your express consent in advance. If you do not give your consent or if you subsequently withdraw it, this will not result in any disadvantages for you. @@ -90,7 +91,7 @@ giving your consent, you can withdraw it at any time (so-called right of withdrawal). Please refer to Section 12 for further information about your right of withdrawal. On the basis of Art. 6(1) Sentence 1(e) GDPR in conjunction with Sect. 3 of the German Federal Data Protection Act - (BDSG), the processing of access data for the provision of daily statistics (see Section 6 e.) + (BDSG), the processing of access data for the provision of daily statistics (see Section 6 f.) is performed as part of the RKI’s duty to inform the public pursuant to Sect. 4(4) of the Act on Successor Agencies to the Federal Health Agency (BGA-NachfG). </p> @@ -107,13 +108,15 @@ The app’s entire system has been programmed to process as little personal data as possible. This means that, when you use exposure logging, warn other users, or retrieve a test result, the system does not need to collect any data that would allow the RKI or other users to infer your - identity, your name, your location or other personal details. The only exception to this is the - optional feature for proving a rapid test result, which allows you to display a confirmation - issued in your name for negative rapid test results (see Section 6 c.). + identity, your name, your location or other personal details. + The only exceptions to this are the feature for proving a rapid test result, which allows you to + display a confirmation issued in your name for negative rapid test results (see Section 6 c.), + and the feature for creating a rapid test profile, which allows you to provide a testing point + with the data required to perform a rapid test (see Section 6 d.). </p> <p> - The app therefore also refrains by default from using analysis tools to evaluate the way you use - it. Only if you expressly agree to voluntarily share data (see Section 5 g.), will certain data + The app refrains by default from using analysis tools to evaluate the way you use + it. Only if you expressly agree to voluntarily share data (see Section 5 h.), will certain data about your use of the app be transmitted to the RKI. </p> <p> @@ -229,8 +232,8 @@ c. Rapid test data </h2> <p> - If you have taken rapid antigen tests at a testing centre, you can retrieve the results of these - through the app. If you choose to use this service, your testing centre will generate an + If you have taken rapid antigen tests at a testing point, you can retrieve the results of these + through the app. If you choose to use this service, your testing point will generate an individual QR code for you to scan with the app. The QR code contains a unique code for your rapid test, and the time you were tested, in encoded form. If, in the event of a negative rapid test result, you wish to have the test result displayed along with your name in the app for @@ -238,7 +241,20 @@ will contain further data provided by you in encoded form. </p> <h2> - d. Event data + d. Rapid test profile +</h2> +<p> + You can store information about yourself in your rapid test profile in the app. The rapid test + profile includes the following fields: first name, last name, date of birth, street and house + number, postcode, town, phone number, email address. The app then converts your data into your + personal QR code, which contains all the data you have entered. Creating a rapid test profile in + the app, and using it at a testing point, are voluntary. You decide yourself which data you + enter in your rapid test profile. The QR code only contains this data. If the testing point + requires more information that is not contained in the QR code, you can also provide this to the + testing point in another way. +</p> +<h2> + e. Event data </h2> <p> If you visit an event (such as a party or concert) or a place (such as a shop or restaurant), @@ -263,7 +279,7 @@ stored on your smartphone. </p> <p> - An entry will also be created in your contact journal by default. Sections 5 f. and 6.f. explain + An entry will also be created in your contact journal by default. Sections 5 g. and 6 g. explain this in more detail. If you do not want to create an entry in your contact journal for an event or place, you can simply switch off this feature using the corresponding toggle switch. </p> @@ -283,7 +299,7 @@ check-out time. </p> <h2> - e. Health data + f. Health data </h2> <p> Health data is any data containing information about a person’s health. @@ -311,7 +327,7 @@ Section 6 explains this in more detail. </p> <h2> - f. Entries in the contact journal + g. Entries in the contact journal </h2> <p> If you use the contact journal to note when and where you met certain people and record certain @@ -331,7 +347,7 @@ tracing purposes, and how you can provide it. </p> <h2> - g. Usage data (data sharing) + h. Usage data (data sharing) </h2> <p> If you enable data sharing, the app will transmit certain data about your use of the app @@ -392,9 +408,9 @@ <p> Participation in data sharing is voluntary. To enable the data sharing feature, the authenticity of your app first needs to be confirmed (please note the further information about this under - Sections 5 i. and 11). + Sections 5 j. and 11). </p> -h. Participation in a survey +i. Participation in a survey </h2> <p> Some app users are offered to participate in a survey by the RKI. This offer to participate in @@ -411,7 +427,7 @@ h. Participation in a survey information about this in Sections 5 i. and 11). </p> <h2> - i. Confirmation of the authenticity of your app + j. Confirmation of the authenticity of your app </h2> <p> Before you can use some of the app’s features, the authenticity of your app first needs to be @@ -445,9 +461,9 @@ h. Participation in a survey </p> <p> For this purpose, the app retrieves an up-to-date positive list from the server system several - times a day. This list contains information from users who have used the warning feature in - their app, which is the official coronavirus app in any country participating in the - transnational warning system (see Section 7). This positive list contains the random IDs of + times a day. This list contains information from users who have used the warning feature in an + official coronavirus app (see Section 7). + This positive list contains the random IDs of users who have activated the warning feature and, if applicable, information about the onset of symptoms. If the users who have activated the warning feature were checked in at events, the positive list also contains the relevant event IDs and the duration of the check-ins (check-in @@ -455,7 +471,7 @@ h. Participation in a survey </p> <p> The random IDs and event IDs on the positive lists also contain a transmission risk value and an - indication of the type of diagnosis (see Section 6 d.). + indication of the type of diagnosis (see Section 6 e.). </p> <p> The app passes the random IDs from the positive list to the COVID-19 exposure @@ -504,7 +520,7 @@ h. Participation in a survey <p> If you have taken a coronavirus test (PCR test or rapid antigen test), you can retrieve your test result via the app. The app will notify you as soon as your test result is available. For - this to work, the testing facility (e.g. testing laboratory or testing centre) needs to be + this to work, the testing facility (e.g. testing laboratory or testing point) needs to be connected to the server system and, as part of the testing procedure, you must have given separate consent to your test result being sent. It is not possible to display test results from testing facilities that are not connected to the app’s server system. If you have not received a @@ -594,7 +610,21 @@ h. Participation in a survey no longer displayed in the app. </p> <h2> - d. Warning others + d. Rapid test profile +</h2> +<p> + The rapid test profile feature offers you the possibility to speed up data collection at + participating testing points. To do this, you can store information about yourself in your rapid + test profile in the app and convert it into your personal QR code, which contains all the data + you have entered. At the testing point, you can present your rapid test profile’s QR code in + your app so that it can be scanned by testing point staff, allowing the data you have provided + to be read. This is a quick and secure way for you to provide the data required to perform a + rapid test. You decide yourself which data you include in your rapid test profile and whether to + present it at testing points. If the testing point requires information that is not contained in + the QR code, you can provide the information to the testing point in another way. +</p> +<h2> + e. Warning others </h2> <p> If you have tested positive for coronavirus and share your random IDs with the app, then it is @@ -669,7 +699,7 @@ h. Participation in a survey which the possible exposure is displayed. </p> <h2> - e. Using the app for information purposes only + f. Using the app for information purposes only </h2> <p> The app automatically receives the daily statistics that appear in the app @@ -680,7 +710,7 @@ h. Participation in a survey of the websites accessed. </p> <h2> - f. Contact journal + g. Contact journal </h2> <p> The contact journal is an additional feature of the app. What you enter in the contact journal @@ -693,7 +723,7 @@ h. Participation in a survey the risk of causing undetected infections. </p> <h2> - g. Data sharing + h. Data sharing </h2> <p> Share Data is an additional feature of the app. The usage data and other voluntary @@ -734,7 +764,7 @@ h. Participation in a survey have met. </p> <h2> - h. Surveys + i. Surveys </h2> <p> The surveys take place on a website outside of the app, which you will be redirected to. The app @@ -742,7 +772,7 @@ h. Participation in a survey survey are described in the information about the survey on the survey website. </p> <h2> - i. Confirmation of the authenticity of your app + j. Confirmation of the authenticity of your app </h2> <p> A feature of your smartphone’s operating system is used to confirm the authenticity of your app. @@ -787,7 +817,7 @@ h. Participation in a survey Each server system merges the positive lists received in this way with its own positive list, which allows the exposure logging feature to also take into account possible exposures involving users of another coronavirus app - (see point 6 d.) The other participating countries proceed in the same way + (see section 6 e.) The other participating countries proceed in the same way with the positive lists provided by the RKI. </p> <p> @@ -941,6 +971,8 @@ h. Participation in a survey 16 days before being automatically deleted. You can also delete these entries yourself at any time. Please note that if entries are added to the contact journal when you check in at an event or place, these will still be stored there even after you delete the associated check-in. + Once you have created your rapid test profile, it will be stored in the app until you delete it + yourself. </p> <h2> b. Data on server systems diff --git a/Corona-Warn-App/src/main/assets/privacy_tr.html b/Corona-Warn-App/src/main/assets/privacy_tr.html index aa5acac5f18ee234ab5659df098ac049f653b610..410bb53ff5f743a34cb576484f0b782d7184834a 100644 --- a/Corona-Warn-App/src/main/assets/privacy_tr.html +++ b/Corona-Warn-App/src/main/assets/privacy_tr.html @@ -1,69 +1,41 @@ <p> - Veri GizliliÄŸi Beyanı -</p> -<p> - Bu Veri GizliliÄŸi Beyanında, Almanya Federal Hükümetinin resmi - Corona-Warn-App’ını kullandığınızda, verilerinizin nasıl iÅŸleneceÄŸini ve - hangi veri koruma haklarına sahip olduÄŸunuzu öğreneceksiniz. + Bu Veri GizliliÄŸi Beyanında, Almanya Federal Hükümetinin resmi Corona-Warn-App’ını + kullandığınızda, verilerinizin nasıl iÅŸleneceÄŸini ve hangi veri koruma haklarına sahip + olduÄŸunuzu öğreneceksiniz. </p> + <p> Burada aÅŸağıdaki konular ele alınmaktadır: </p> -<p> - <strong>1. Corona-Warn-App’ın yayımcısı kimdir?</strong> -</p> -<p> - <strong>2. Uygulamanın kullanılması isteÄŸe baÄŸlı mı?</strong> -</p> -<p> - <strong>3. Verileriniz iÅŸlenmesinde hangi yasal dayanaklar söz konusudur?</strong> -</p> -<p> - <strong>4. Uygulama kimleri hedefler?</strong> -</p> -<p> - <strong>5. Hangi veriler iÅŸlenir?</strong> -</p> -<p> - <strong>6. Verileriniz niçin iÅŸleniyor?</strong> -</p> -<p> - <strong>7. Sınır ötesi uyarı sistemi nasıl çalışır?</strong> -</p> -<p> - <strong>8. Uygulamanın hangi izinlere gerek duyar?</strong> -</p> -<p> - <strong>9. Verileriniz ne zaman silinir?</strong> -</p> -<p> - <strong>10. Verileriniz kime aktarılır?</strong> -</p> -<p> - <strong>11. Verileriniz AB dışındaki ülkelere aktarılacak mı?</strong> -</p> -<p> - <strong>12. VerdiÄŸiniz rıza beyanını nasıl geri alabilirsiniz?</strong> -</p> -<p> - <strong>13. BaÅŸka hangi veri koruma haklarına sahipsiniz?</strong> -</p> -<p> - <strong>14. Veri koruma görevlisi ve iletiÅŸim</strong> -</p> -<p> - Bu metnin tüm kullanıcılar için anlaşılabilir olması amacıyla, mümkün - olduÄŸunca basit ve teknik terimler içermeyen bir metin hazırladık. +<ol> + <li><strong>Corona-Warn-App’ın yayımcısı kimdir?</strong></li> + <li><strong>Uygulamanın kullanılması isteÄŸe baÄŸlı mı?</strong></li> + <li><strong>Verileriniz iÅŸlenmesinde hangi yasal dayanaklar söz konusudur?</strong></li> + <li><strong>Uygulama kimleri hedefler?</strong></li> + <li><strong>Hangi veriler iÅŸlenir?</strong></li> + <li><strong>Verileriniz niçin iÅŸleniyor?</strong></li> + <li><strong>Sınır ötesi uyarı sistemi nasıl çalışır?</strong></li> + <li><strong>Uygulamanın hangi izinlere gerek duyar?</strong></li> + <li><strong>Verileriniz ne zaman silinir?</strong></li> + <li><strong>Verileriniz kime aktarılır?</strong></li> + <li><strong>Verileriniz AB dışındaki ülkelere aktarılacak mı?</strong></li> + <li><strong>VerdiÄŸiniz rıza beyanını nasıl geri alabilirsiniz?</strong></li> + <li><strong>BaÅŸka hangi veri koruma haklarına sahipsiniz?</strong></li> + <li><strong>Veri koruma görevlisi ve iletiÅŸim</strong></li> +</ol> +<p> + Bu metnin tüm kullanıcılar için anlaşılabilir olması amacıyla, mümkün olduÄŸunca basit ve teknik + terimler içermeyen bir metin hazırladık. </p> <h1> 1. Corona-Warn-App’ın yayımcısı kimdir? </h1> <p> - Bu Uygulama, Almanya Federal Hükümeti için Robert Koch-Institut (Robert - Koch Enstitüsü) (<strong>RKI</strong>) tarafından yayımlanmaktadır. RKI, - ayrıca kiÅŸisel verilerinizin, veri gizliliÄŸi kurallarına uygun olarak iÅŸlenmesinden - de sorumludur. + Bu Uygulama, Almanya Federal Hükümeti için Robert Koch-Institut (Robert Koch Enstitüsü) + (<strong>RKI</strong>) tarafından yayımlanmaktadır. RKI, ayrıca kiÅŸisel verilerinizin, veri + gizliliÄŸi kurallarına uygun olarak iÅŸlenmesinden de sorumludur. </p> + <p> Uygulama ile yapılan korona testiniz pozitif çıkmışsa ve sınır ötesi bir uyarı tetiklediÄŸinizde, katılımcı ülkelerdeki resmi Korona uygulamalarının kullanıcılarıyla bir karşılaÅŸma söz konusu @@ -78,7 +50,7 @@ Uygulamanın kullanılması isteÄŸe baÄŸlıdır. Uygulamayı yüklemeniz, Uygulamanın hangi iÅŸlevlerini kullanmanız ve verileri diÄŸer kiÅŸilerle paylaÅŸmanız noktasında yalnızca siz karar verirsiniz. KiÅŸisel verilerinizin RKI’ye veya diÄŸer kullanıcılara aktarılmasını gerektiren Uygulamanın tüm - iÅŸlevleri, sizden önceden açıkça rızanızı vermenizi gerektirir. Rızanızı vermezseniz veya + ana iÅŸlevleri, sizden önceden açıkça rızanızı vermenizi gerektirir. Rızanızı vermezseniz veya sonradan bu rızayı geri alırsanız, bu durum sizin için bir sakınca doÄŸurmaz. </p> <h1> @@ -91,7 +63,7 @@ ise GVKT madde 9, fıkra 2, bent a’dır. Daha önce vermiÅŸ olduÄŸunuz rızanızı, istediÄŸi zaman geri alabilirsiniz (geri alma hakkı). Onayınız geri alma hakkı ile ilgili ayrıntılı bilgileri Madde 12’de bulabilirsiniz. Günlük istatistiklerin saÄŸlanması için eriÅŸim verilerinin iÅŸlenmesi (bkz. - Madde 6 d.), GVKT madde 6, fıkra 1, bent e ile baÄŸlantılı olarak BGA-NachfG (Federal SaÄŸlık + Madde 6 f.), GVKT madde 6, fıkra 1, bent e ile baÄŸlantılı olarak BGA-NachfG (Federal SaÄŸlık Kurumu Halef KuruluÅŸları hakkında Kanun) madde 4, fıkra 4 uyarınca RKI tarafından toplumun bilgilendirilmesi kapsamında gerçekleÅŸir. </p> @@ -99,8 +71,7 @@ 4. Uygulama kimleri hedefler? </h1> <p> - Uygulama, en az 16 yaşında olan ve Almanya’da yaÅŸayan kiÅŸilere yönelik - hazırlanmıştır. + Uygulama, en az 16 yaşında olan ve Almanya’da yaÅŸayan kiÅŸilere yönelik hazırlanmıştır. </p> <h1> 5. Hangi veriler iÅŸlenir? @@ -111,28 +82,31 @@ test sonucunun alınması için, RKI’nin veya diÄŸer kullanıcıların sizin kimliÄŸinizi, adınızı, konumunuzu veya diÄŸer kiÅŸisel bilgilerinizi öğrenmesine olanak tanıyan verileri toplamamaktadır. Kendi adınıza düzenlenen negatif hızlı test sonucu için onayı görüntüleyebileceÄŸiniz (bkz. Madde - 6c) Opsiyonel “hızlı test sonucunu kanıtlama†fonksiyonu için istisna söz konusudur. + 6c) “hızlı test sonucunu kanıtlama†fonksiyonu için istisna ve test merkezinde hızlı testin + uygulanması için gereken verilerin hazırlandığı “hızlı test profiliâ€oluÅŸturma fonksiyonu söz + konusudur (bunun için bkz. Madde 6d.). </p> + <p> Dolayısıyla Uygulama, standart olarak analiz araçları üzerinden kullanıcı davranışınızın herhangi bir deÄŸerlendirmesini yapmaz. Sadece isteÄŸe baÄŸlı veri bağışına açıkça onay vermeniz durumunda, Uygulama kullanımınıza iliÅŸkin belirli veriler, RKI’ye aktarılacaktır (bkz. Madde 5 - g.). + h.). </p> + <p> - Uygulama tarafından iÅŸlenen veriler aÅŸağıdaki kategorilerde - sınıflandırılabilir: + Uygulama tarafından iÅŸlenen veriler aÅŸağıdaki kategorilerde sınıflandırılabilir: </p> <h2> a. EriÅŸim verileri </h2> + <p> Uygulama, RKI’nin sunucu sistemiyle (bundan böyle: <strong>sunucu sistemi</strong>) her veri - alışveriÅŸinde, sunucu sistemi - eriÅŸim verileri olarak adlandırılan verileri iÅŸler. Bu iÅŸlem, Uygulamanın - güncel verilere (örneÄŸin uyarılar için) eriÅŸebilmesi veya akıllı telefonda - saklanan belirli verileri sunucu sistemine aktarabilmesi için gereklidir. - EriÅŸim verileri aÅŸağıdaki verilerden oluÅŸmaktadır: + alışveriÅŸinde, sunucu sistemi eriÅŸim verileri olarak adlandırılan verileri iÅŸler. Bu iÅŸlem, + Uygulamanın güncel verilere (örneÄŸin uyarılar için) eriÅŸebilmesi veya akıllı telefonda saklanan + belirli verileri sunucu sistemine aktarabilmesi için gereklidir. EriÅŸim verileri aÅŸağıdaki + verilerden oluÅŸmaktadır: </p> <ul> <li> @@ -149,31 +123,29 @@ </li> </ul> <p> - Bu eriÅŸim verileri, Uygulamanın ve sunucu sisteminin teknik açıdan - iÅŸletimini sürdürmek ve güvence altına almak için iÅŸlenmektedir. Bu süreçte - siz, Uygulamanın kullanıcısı olarak ÅŸahsen tanımlanmazsınız ve sizin için - bir kullanıcı profili oluÅŸturulmaz. IP adresi, kullanım iÅŸlemi bitiminden - sonra saklanmaz. + Bu eriÅŸim verileri, Uygulamanın ve sunucu sisteminin teknik açıdan iÅŸletimini sürdürmek ve + güvence altına almak için iÅŸlenmektedir. Bu süreçte siz, Uygulamanın kullanıcısı olarak ÅŸahsen + tanımlanmazsınız ve sizin için bir kullanıcı profili oluÅŸturulmaz. IP adresi, kullanım iÅŸlemi + bitiminden sonra saklanmaz. </p> + <p> - Bir kullanım iÅŸlemi sırasında verilerinizin IP adresinize yetkisiz - atanmasını önlemek için, Uygulama, sunucu sistemine sadece özel bir giriÅŸ - sunucusu üzerinden eriÅŸir. Bunun ardından giriÅŸ sunucusu, Uygulama - tarafından talep edilen veya iletilen verileri, IP adresi olmaksızın, - yetkili sunucuya iletir, bu sayede IP adresi sunucu sisteminde iÅŸlenmez. + Bir kullanım iÅŸlemi sırasında verilerinizin IP adresinize yetkisiz atanmasını önlemek için, + Uygulama, sunucu sistemine sadece özel bir giriÅŸ sunucusu üzerinden eriÅŸir. Bunun ardından giriÅŸ + sunucusu, Uygulama tarafından talep edilen veya iletilen verileri, IP adresi olmaksızın, yetkili + sunucuya iletir, bu sayede IP adresi sunucu sisteminde iÅŸlenmez. </p> <h2> b. Maruz kalma verileri </h2> + <p> - iPhone’unuzun COVID-19 bildirim sistemini (orada “maruz kalma bildirimleri†- olarak adlandırılır) veya Android akıllı telefonunuzun (orada “COVID-19 - temas bildirimleri†olarak adlandırılır) etkinleÅŸtirdiÄŸinizde, akıllı - telefonunuz, Bluetooth üzerinden maruz kalma verilerini göndermeye baÅŸlar - ve bu veriler yakınınızda bulunan diÄŸer akıllı telefonlar tarafından - kaydedilir. Öte yandan, kendi akıllı telefonunuz da diÄŸer akıllı - telefonlardan maruz kalma verileri alır. Gönderilen maruz kalma verileri - ÅŸunları içerir: + iPhone’unuzun COVID-19 bildirim sistemini (orada “maruz kalma bildirimleri†olarak adlandırılır) + veya Android akıllı telefonunuzun (orada “COVID-19 temas bildirimleri†olarak adlandırılır) + etkinleÅŸtirdiÄŸinizde, akıllı telefonunuz, Bluetooth üzerinden maruz kalma verilerini göndermeye + baÅŸlar ve bu veriler yakınınızda bulunan diÄŸer akıllı telefonlar tarafından kaydedilir. Öte + yandan, kendi akıllı telefonunuz da diÄŸer akıllı telefonlardan maruz kalma verileri alır. + Gönderilen maruz kalma verileri ÅŸunları içerir: </p> <ul> <li> @@ -187,8 +159,7 @@ </li> </ul> <p> - Kaydedilen karşılaÅŸmalardaki maruz kalma verileri, ek olarak ÅŸunları - içerir: + Kaydedilen karşılaÅŸmalardaki maruz kalma verileri, ek olarak ÅŸunları içerir: </p> <ul> <li> @@ -206,16 +177,15 @@ ÅŸekilde gönderdiÄŸiniz maruz kalma verileriniz, diÄŸer Uygulama kullanıcılarının akıllı telefonları tarafından kaydedildiÄŸinde, iÅŸlenmeye baÅŸlanır. </p> + <p> - Lütfen dikkate alın: COVID-19 bildirim sistemi, iÅŸletim sisteminizin bir - iÅŸlevidir. Dolayısıyla bu sistemin saÄŸlayıcıları ve sorumluları, Apple - (iPhone’unuz varsa) ve Google’dır (Android akıllı telefonunuz varsa). Bu - baÄŸlamda verilerin iÅŸlenmesi, bu ÅŸirketlerin veri koruma yönetmeliklerine - tabidir ve RKI’nin sorumluluk ve etki alanı dışındadır. Ä°ÅŸletim - sisteminizin sürümüne ve ayarlarına baÄŸlı olarak, fiili gösterimler, - kullanım adımları ve ayar seçenekleri, bu Veri GizliliÄŸi Beyanındaki - sunumdan farklı olabilirler. Söz konusu üreticiler, size daha ayrıntılı - bilgi saÄŸlamaktadır: + Lütfen dikkate alın: COVID-19 bildirim sistemi, iÅŸletim sisteminizin bir iÅŸlevidir. Dolayısıyla + bu sistemin saÄŸlayıcıları ve sorumluları, Apple (iPhone’unuz varsa) ve Google’dır (Android + akıllı telefonunuz varsa). Bu baÄŸlamda verilerin iÅŸlenmesi, bu ÅŸirketlerin veri koruma + yönetmeliklerine tabidir ve RKI’nin sorumluluk ve etki alanı dışındadır. Ä°ÅŸletim sisteminizin + sürümüne ve ayarlarına baÄŸlı olarak, fiili gösterimler, kullanım adımları ve ayar seçenekleri, + bu Veri GizliliÄŸi Beyanındaki sunumdan farklı olabilirler. Söz konusu üreticiler, size daha + ayrıntılı bilgi saÄŸlamaktadır: </p> <ul> <li> @@ -240,8 +210,19 @@ kanıtlama amaçları için isminizle görüntülemek için kare kodda kodlanmış halde tarafınızca kaydedilen diÄŸer veriler bulunur. </p> +<h2>d. Hızlı test profili</h2> +<p>Uygulamada hızlı test profilinize kiÅŸisel bilgiler kaydedebilirsiniz. Hızlı test profili ÅŸu + alanları içerir: Ad, soyadı, doÄŸum tarihi, sokak ve hane numarası, posta kodu, ÅŸehir, telefon + numarası, e-posta adresi. Uygulama verilerinizi tarafınızca belirtilen tüm verileri içeren + kiÅŸisel kara kodunuza dönüştürür. Uygulamada hızlı test profilinin oluÅŸturulması ve bunun test + merkezinde kullanılması isteÄŸe baÄŸlıdır. Hızlı test profilinize hangi verileri kaydedeceÄŸinizi + kendiniz belirleyebilirsiniz. Sadece bu veriler kare kodda bulunur. Test merkezinde kare kodunda + bulunmayan baÅŸka bilgilerin gerekli olması halinde bu bilgileri test merkezine baÅŸka ÅŸekilde de + ulaÅŸtırabilirsiniz. +</p> + <h2> - d. Olay verileri + e. Olay verileri </h2> <p> Bir olayı (örneÄŸin bir etkinlik, bir parti veya konser) veya bir konumu (örneÄŸin bir maÄŸaza veya @@ -268,7 +249,7 @@ </p> <p> Bunun yanı sıra temas günlüğünüzde standart olarak bir veri giriÅŸi oluÅŸturulur. Bu konudaki - ayrıntılar Madde 5.e. ve 6.e. açıklanmıştır. Temas günlüğünüzde bir olay veya konum için bir + ayrıntılar Madde 5.g. ve 6.g. açıklanmıştır. Temas günlüğünüzde bir olay veya konum için bir veri giriÅŸi oluÅŸturmak istemiyorsanız, Uygulamadaki ilgili kaydırıcıyı kullanarak bu iÅŸlevi kolayca devre dışı bırakabilirsiniz. </p> @@ -289,13 +270,12 @@ oluÅŸturmaya ve bir olay veya konumda giriÅŸ denetimi yaptırmaya siz kendiniz karar verirsiniz. </p> <h2> - e. SaÄŸlık verileri + f. SaÄŸlık verileri </h2> <p> - SaÄŸlık verileri, bir kiÅŸinin saÄŸlığı hakkında bilgiler içeren tüm - verilerdir. Bunlar, yalnızca bir kiÅŸinin eski ve güncel hastalıklarıyla - ilgili bilgileri deÄŸil, aynı zamanda hastalık riski (örneÄŸin bir kiÅŸinin - Koronavirüs ile enfekte olma riski) ile ilgili bilgilerdir. Uygulama, + SaÄŸlık verileri, bir kiÅŸinin saÄŸlığı hakkında bilgiler içeren tüm verilerdir. Bunlar, yalnızca + bir kiÅŸinin eski ve güncel hastalıklarıyla ilgili bilgileri deÄŸil, aynı zamanda hastalık riski + (örneÄŸin bir kiÅŸinin Koronavirüs ile enfekte olma riski) ile ilgili bilgilerdir. Uygulama, saÄŸlık verilerini aÅŸağıdaki durumlarda iÅŸler: </p> <ul> @@ -317,8 +297,9 @@ Ayrıntılar 6. Maddede açıklanmıştır. </p> <h2> - f. Temas güncesindeki veriler + g. Temas güncesindeki veriler </h2> + <p> Temas günlüğünüze, hangi kiÅŸilerle ne zaman ve nerede karşılaÅŸtığınızı ve karşılaÅŸmanın koÅŸullarını veya kiÅŸiler ve konumlar ile ilgili iletiÅŸim bilgilerini not ederseniz, bu bilgiler @@ -328,6 +309,7 @@ çıkarsa ve saÄŸlık kurumu sizden temas takibinde yardım etmenizi rica ederse, saÄŸlık kurumuna gerekli bilgileri hızlı bir ÅŸekilde aktarabilirsiniz. </p> + <p> Temas güncesinin kullanılması isteÄŸe baÄŸlıdır. Veri giriÅŸlerinin temas güncesine nasıl kaydedileceÄŸine siz karar verirsiniz. Bu açıdan veri giriÅŸlerinizden de siz sorumlusunuz. @@ -336,10 +318,12 @@ aktarımı yapılmamalıdır. Yetkili saÄŸlık kurumu, temas baÄŸlantılarınızı takip etmek isterse, sizden hangi bilgilere gerek duyduÄŸunu ve sizin bunları nasıl saÄŸlayacağınızı size söyleyecektir. + </p> <h2> - g. Veri bağışı + h. Kullanım verileri (veri bağışı) </h2> + <p> Veri bağışını etkinleÅŸtirdiÄŸinizde Uygulama, Uygulama kullanımınıza iliÅŸkin belirli verileri günde bir kez RKI’ye aktarır (bundan böyle: Kullanım verileri). Bu kullanım verileri, Uygulama @@ -361,8 +345,7 @@ Uygulama üzerinden test sonucunuzu aldıysanız: </p> <ul> - <li>Test sonucunun pozitif mi yoksa negatif mi olduÄŸu. - </li> + <li>Test sonucunun pozitif mi yoksa negatif mi olduÄŸu.</li> <li>Test kaydı sırasında hangi riskin görüntülendiÄŸi.</li> <li>Son riskli karşılaÅŸmadan ve bunun Uygulamada görüntülenmesinden test kaydına kadar geçen süre. @@ -397,11 +380,12 @@ <p> Veri bağışına katılım, gönüllü olarak gerçekleÅŸmektedir. Veri bağışının etkinleÅŸtirilmesi, Uygulamanızın orijinalliÄŸinin doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için - bkz. Madde 5 h. ve 11.). + bkz. Madde 5 j. ve 11.). </p> <h2> - h. Ankete katılım + i. Ankete katılım </h2> + <p> Uygulamada bazı kullanıcılara bir RKI anketine katılma olanağı verilir. Ankete katılma teklifi genelde, Uygulamada kaydedilen belirli bazı olaylara (örneÄŸin yüksek risk görüntülenmesi) baÄŸlı @@ -409,15 +393,17 @@ iyileÅŸtirmesine ve örneÄŸin Uygulama üzerinden gönderilen uyarıların enfeksiyonu önlemeye katkı verip vermediklerini ve nasıl verdiklerini anlamasına yardımcı olursunuz. </p> + <p> Ankete katılım isteÄŸe baÄŸlıdır. Bir ankete katılmaya ve kullanım verilerinin RKI’ye aktarılmasına, siz kendiniz karar verirsiniz. Bu anketler, yönlendirileceÄŸiniz Uygulama dışındaki bir web sitesinde gerçekleÅŸtirilir. Ankete katılım, Uygulamanızın orijinalliÄŸinin - doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için bkz. Madde 5 h. ve 11.). + doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için bkz. Madde 5 j. ve 11.). </p> <h2> - i. Uygulamanızın orijinalliÄŸinin doÄŸrulanması + j. Uygulamanızın orijinalliÄŸinin doÄŸrulanması </h2> + <p> Uygulamadaki bazı iÅŸlevler, Uygulamanızın orijinalliÄŸinin önceden kontrol edilmesini ve RKI için onaylanmasını gerektirir. Orijinal ürün kontrolü, özellikle Uygulamanın manipüle edilmiÅŸ veya @@ -440,50 +426,59 @@ <h2> a. Risk deÄŸerlendirmesi </h2> + <p> - Uygulamanın ana iÅŸlevlerinden biri, risk deÄŸerlendirmesidir. Bunun - görevleri, Korona testi pozitif çıkan kiÅŸilerle olası maruz kalmaları - (riskli karşılaÅŸmalar) konusunda sizi sınır ötesi ortamda da uyarmak, - kiÅŸisel enfeksiyon riskinizi hesaplamak ve size bu baÄŸlamda davranış ve + Uygulamanın ana iÅŸlevlerinden biri, risk deÄŸerlendirmesidir. Bunun görevleri, Korona testi + pozitif çıkan kiÅŸilerle olası maruz kalmaları (riskli karşılaÅŸmalar) konusunda sizi sınır ötesi + ortamda da uyarmak, kiÅŸisel enfeksiyon riskinizi hesaplamak ve size bu baÄŸlamda davranış ve saÄŸlık bilgileri temin etmektir. </p> + <p> - Bu amaç doÄŸrultusunda Uygulama, sınır ötesi uyarı sistemine katılan bir ülkenin resmi Korona - uygulamaları aracılığıyla bir uyarı tetikleyen kullanıcılara ait bilgileri içeren güncel bir - pozitif listeyi sunucu sisteminden günde birkaç kez çağırır (bkz. Madde 7). Bu pozitif liste, - uyarı tetikleyen kullanıcılara ait rastgele kimlik numaralarını ve varsa semptomların - baÅŸlangıcına iliÅŸkin bilgileri içerir. Uyarı tetikleyen kullanıcılar bir olayda giriÅŸ denetimi - yaptırırlarsa, bu durumda pozitif liste, ilgili olay kimliklerini ve olayda kalma süresini - (giriÅŸ ve çıkış saatleri) içerir. + Bu amaç doÄŸrultusunda Uygulama, resmi Korona uygulamaları aracılığıyla bir uyarı tetikleyen + kullanıcılara ait bilgileri içeren güncel bir pozitif listeyi sunucu sisteminden günde birkaç + kez çağırır (bkz. Madde 7). Bu pozitif liste, uyarı tetikleyen kullanıcılara ait rastgele kimlik + numaralarını ve varsa semptomların baÅŸlangıcına iliÅŸkin bilgileri içerir. Uyarı tetikleyen + kullanıcılar bir olayda giriÅŸ denetimi yaptırırlarsa, bu durumda pozitif liste, ilgili olay + kimliklerini ve olayda kalma süresini (giriÅŸ ve çıkış saatleri) içerir. </p> <p> Pozitif listedeki rastgele kimlik numaralarını ve olay kimliklerini, ek olarak bir taşıma riski - deÄŸeri ve tanı tipi hakkında bilgi de içerir (bkz. Madde 6 c.). + deÄŸeri ve tanı tipi hakkında bilgi de içerir (bkz. Madde 6 e.). </p> <p> - Uygulama pozitif listeden aldığı bu rastgele kimlik numaralarını, COVID-19 - bildirim sistemine aktarır ve bu sistem bu kodları, karşılaÅŸmalarınızda - kaydedilen rastgele kimlik numaraları ile karşılaÅŸtırır. COVID-19 bildirim - sistemi bir eÅŸleÅŸme saptarsa, söz konusu maruz kalma için kaydedilen maruz - kalma verilerini Uygulamaya aktarır. Bu maruz kalma verileri ve pozitif - listedeki bilgiler (taşıma riski deÄŸeri, semptomların baÅŸlangıcına iliÅŸkin - bilgiler) enfeksiyon riskinizi belirlemek için Uygulama tarafından - deÄŸerlendirmeye alınır. Bu veriler için olan deÄŸerlendirme kuralları, - RKI’nin güncel bilimsel bulgularına (örn. temas süresinin enfeksiyon riski - üzerindeki etkisi) dayanmaktadır. Yeni bulgular olması durumunda RKI, - Uygulamanın deÄŸerlendirme ayarları üzerinden bu deÄŸerlendirme kurallarını - güncelleyebilmektedir. Bu durumda, yeni deÄŸerlendirme ayarları pozitif - listeyle birlikte Uygulamaya aktarılır. + Uygulama pozitif listeden aldığı bu rastgele kimlik numaralarını, COVID-19 bildirim sistemine + aktarır ve bu sistem bu kodları, karşılaÅŸmalarınızda kaydedilen rastgele kimlik numaraları ile + karşılaÅŸtırır. COVID-19 bildirim sistemi bir eÅŸleÅŸme saptarsa, söz konusu maruz kalma için + kaydedilen maruz kalma verilerini Uygulamaya aktarır. Bu maruz kalma verileri ve pozitif + listedeki bilgiler (taşıma riski deÄŸeri, semptomların baÅŸlangıcına iliÅŸkin bilgiler) enfeksiyon + riskinizi belirlemek için Uygulama tarafından deÄŸerlendirmeye alınır. Bu veriler için olan + deÄŸerlendirme kuralları, RKI’nin güncel bilimsel bulgularına (örn. temas süresinin enfeksiyon + riski üzerindeki etkisi) dayanmaktadır. Yeni bulgular olması durumunda RKI, Uygulamanın + deÄŸerlendirme ayarları üzerinden bu deÄŸerlendirme kurallarını güncelleyebilmektedir. Bu durumda, + yeni deÄŸerlendirme ayarları pozitif listeyle birlikte Uygulamaya aktarılır. </p> + <p> - Enfeksiyon riski, yalnızca Uygulama içinde hesaplanır ve COVID-19 bildirim - sistemine veya diÄŸer alıcılara (RKI, Almanya’daki diÄŸer saÄŸlık kurumları - veya diÄŸer ülkeler, Apple, Google ve diÄŸer üçüncü taraflar da dahil olmak - üzere) aktarılmaz. + Enfeksiyon riski, yalnızca Uygulama içinde hesaplanır ve COVID-19 bildirim sistemine veya diÄŸer + alıcılara (RKI, Almanya’daki diÄŸer saÄŸlık kurumları veya diÄŸer ülkeler, Apple, Google ve diÄŸer + üçüncü taraflar da dahil olmak üzere) aktarılmaz. +</p> + +<p> + Sizin için bir enfeksiyon riski hesaplanırsa, bu deÄŸer Uygulamada görüntülenecektir. + Görüntülenen bu risk deÄŸeri yüksek ise, bunun anlamı, sizin Korona testi pozitif çıkan ve bir + uyarı tetikleyen diÄŸer kullanıcılarla bir veya daha çok kez karşılaÅŸmış olmanızdır. Son 14 gün + için hesaplanan risk, iletiÅŸim günlüğünün takvim görünümünde görüntülenir. Lütfen riskin + kaynağıyla ilgili yanlış çıkarımlar yapmaktan kaçının: Belli bir gün için hesaplanan ve + görüntülenen bir risk, tanımadığınız kullanıcılarla farkına varmadığınız bir karşılaÅŸmaya kadar + geri gidebilir ve mutlaka iletiÅŸim günlüğüne girdiÄŸiniz kiÅŸiler veya yerlerle ilgili olması + gerekmez. </p> <h2> b. Test sonucu çağırın </h2> + <p> Bir korona testi (PCR testi ya da hızlı antijen testi) yaptırdıysanız, test sonucunuzu Uygulama üzerinden görüntüleyebilirsiniz. Test sonucunuz hazır olur olmaz, Uygulama sizi bu konuda @@ -493,18 +488,11 @@ olmayan test kuruluÅŸları tarafından gönderilen test sonuçları görüntülenemez. Bir QR kod almadıysanız, bu iÅŸlevi yine aynı ÅŸekilde kullanamazsınız. </p> -<p> - Sizin için bir enfeksiyon riski hesaplanırsa, bu deÄŸer Uygulamada görüntülenecektir. - Görüntülenen bu risk deÄŸeri yüksek ise, bunun anlamı, sizin Korona testi pozitif çıkan ve bir - uyarı tetikleyen diÄŸer kullanıcılarla bir veya daha çok kez karşılaÅŸmış olmanızdır. Son 14 gün - için hesaplanan risk, iletiÅŸim günlüğünün takvim görünümünde görüntülenir. Lütfen riskin - kaynağıyla ilgili yanlış çıkarımlar yapmaktan kaçının: Belli bir gün için hesaplanan ve - görüntülenen bir risk, tanımadığınız kullanıcılarla farkına varmadığınız bir karşılaÅŸmaya kadar - geri gidebilir ve mutlaka iletiÅŸim günlüğüne girdiÄŸiniz kiÅŸiler veya yerlerle ilgili olması - gerekmez.</p> + <p> <u>QR kodu tarayın</u> </p> + <p> Test sonucunu Uygulama aracılığıyla çağırmak için, bu QR kodu akıllı telefonunuzun kamerasıyla taramanız gerekir. Bu QR kod, bir kod numarası içerir, bu kod numarası tarama iÅŸlemiyle okunur @@ -522,9 +510,11 @@ geçilmiÅŸ olur. </p> + <p> <u>Test sonucunun saklanması</u> </p> + <p> Test sonucunuz test kuruluÅŸları tarafından hazır olduÄŸunda, bu sonucu hemen ilgili karma kod numarası ile birlikte RKI tarafından iÅŸletilen test sonuçları veri tabanına kaydedilmek üzere @@ -532,28 +522,26 @@ kuruluÅŸu, aynı matematik yöntemini kullanarak, size verilen QR kodda yer alan kod numarası bazında karma kod numarasını oluÅŸturur. </p> + <p> - <u>Test sonucunun çaÄŸrılması</u> + <u>Test sonucunun çaÄŸrılması </u> </p> + <p> - Uygulama, düzenli olarak Uygulamada kaydedilmiÅŸ olan belirteci kullanarak - sunucu sisteminden kayıtlı testinizin durumunu sorgular. Sunucu sistemi, - Uygulamaya testinizin güncel durumunu (sonuç hazır / sonuç hazır deÄŸil) - paylaşır. Test sonucunuz hazır olduÄŸunda, ilgili tanı (Korona pozitif veya - Korona negatif) Uygulamaya iletilir. Uygulamadaki test durumu - bildirimlerini etkinleÅŸtirdiyseniz (“Ayarlar†> “Bildirimler†altında), - söz konusu bildirimi alırsınız. Ancak Uygulamayı açtıktan sonra, test - sonucu size gösterilir. + Uygulama, düzenli olarak Uygulamada kaydedilmiÅŸ olan belirteci kullanarak sunucu sisteminden + kayıtlı testinizin durumunu sorgular. Sunucu sistemi, Uygulamaya testinizin güncel durumunu + (sonuç hazır / sonuç hazır deÄŸil) paylaşır. Test sonucunuz hazır olduÄŸunda, ilgili tanı (Korona + pozitif veya Korona negatif) Uygulamaya iletilir. Uygulamadaki test durumu bildirimlerini + etkinleÅŸtirdiyseniz (“Ayarlar†> “Bildirimler†altında), söz konusu bildirimi alırsınız. + Ancak Uygulamayı açtıktan sonra, test sonucu size gösterilir. </p> <p> - Korona testiniz pozitif çıkmışsa, Uygulama, tekrar belirteci kullanarak - sunucu sisteminden bir TAN (iÅŸlem numarası) ister. Bu TAN kodu, diÄŸer - kullanıcılara yanlış uyarı verilmesini önlemek için gereklidir. Bu amaçla - sunucu sistemi, belirteci tekrar karma kod numarasına atar ve test - sonuçları veri tabanından bu karma kod numarası için gerçekten bir pozitif - test sonucunun mevcudiyetini doÄŸrulamasını ister. Ä°lgili doÄŸrulama - alındığında, sunucu sistemi TAN’ı oluÅŸturur ve bunu uygulamaya iletir. Bu - TAN’ın bir kopyası sunucu sisteminde kalır. + Korona testiniz pozitif çıkmışsa, Uygulama, tekrar belirteci kullanarak sunucu sisteminden bir + TAN (iÅŸlem numarası) ister. Bu TAN kodu, diÄŸer kullanıcılara yanlış uyarı verilmesini önlemek + için gereklidir. Bu amaçla sunucu sistemi, belirteci tekrar karma kod numarasına atar ve test + sonuçları veri tabanından bu karma kod numarası için gerçekten bir pozitif test sonucunun + mevcudiyetini doÄŸrulamasını ister. Ä°lgili doÄŸrulama alındığında, sunucu sistemi TAN’ı oluÅŸturur + ve bunu uygulamaya iletir. Bu TAN’ın bir kopyası sunucu sisteminde kalır. </p> <h2> c. Hızlı test sonucunun kanıtı @@ -586,9 +574,26 @@ Uygulamadan hemen silinir. DiÄŸer hızlı test verileriniz Uygulamada poztif çıkan hızlı test sonucu görüntülenmediÄŸi anda (kodlar, test zamanı) silinir. </p> + +<h2> + d. Hızlı test profili +</h2> +<p> + Hızlı test profili katılımcı test merkezlerinde verilerin toplanmasının hızlandırılması olanağı + saÄŸlar. Bunun için uygulamada hızlı test profilinize kiÅŸisel bilgiler kaydedebilir ve bunları + verilen tüm bilgileri içeren kiÅŸisel kare kodunuza dönüştürebilirsiniz. Test merkezinde + uygulamanızdaki hızlı test profilinizin kare kodunu gösterebilir, böylece bu test merkezi + tarafından taranır ve tarafınızca verilen bilgiler okunabilir. Hızlı testlerin uygulanması için + gereken veriler bu ÅŸekilde hızlı ve güvenli bir ÅŸekilde hazırlanır. Hızlı test profiline hangi + bilgileri vereceÄŸinizi ve bunları test merkezlerinde gösterip göstermeyeceÄŸinizi siz + belirlersiniz. Test merkezinde gereken ve kare kodunda bulunmayan bilgileri test merkezinde + farklı ÅŸekilde ibraz edebilirsiniz. +</p> + <h2> - d. DiÄŸerlerini uyarın + e. DiÄŸerlerini uyarın </h2> + <p> Korona testiniz pozitif çıkmışsa ve siz rastgele kimlik numaralarınızı Uygulama aracılığıyla paylaşırsanız, kullanıcılarla bir karşılaÅŸma söz konusu olduÄŸunda, bu kiÅŸilerin uyarılması @@ -607,7 +612,7 @@ Semptomların baÅŸlangıcına iliÅŸkin olası bilgi </li> <li> - TAN’ınız (bkz. Madde 6 b.) + TAN’ınız (bkz. Madde 6 b.) </li> </ul> <p> @@ -620,17 +625,19 @@ enfeksiyon riski o kadar düşük olur. Bu ek taşıma riski deÄŸerleri, diÄŸer kullanıcılar için enfeksiyon olasılığının daha kesin bir ÅŸekilde belirlenmesini mümkün kılar. </p> + <p> - Uygulamada sorgulanan semptomların baÅŸlangıcına iliÅŸkin bilginin verilmesi, - isteÄŸe baÄŸlıdır. Ancak bu bilgi, taşıma riski deÄŸerinin daha da kesin bir - ÅŸekilde hesaplanmasında yardımcı olmaktadır. Bu bilgiyi saÄŸlamadığınızda, - enfeksiyonun tipik seyri varsayılarak taşıma riski deÄŸeri hesaplanır, yani - rastgele kimlik numarası kullanımından sonra geçen süre ne kadar uzunsa, + Uygulamada sorgulanan semptomların baÅŸlangıcına iliÅŸkin bilginin verilmesi, isteÄŸe baÄŸlıdır. + Ancak bu bilgi, taşıma riski deÄŸerinin daha da kesin bir ÅŸekilde hesaplanmasında yardımcı + olmaktadır. Bu bilgiyi saÄŸlamadığınızda, enfeksiyonun tipik seyri varsayılarak taşıma riski + deÄŸeri hesaplanır, yani rastgele kimlik numarası kullanımından sonra geçen süre ne kadar uzunsa, ilgili taşıma riski deÄŸeri de o kadar küçük olur. </p> + <p> <u>Uygulama üzerinden test sonucunuzu almadıysanız:</u> </p> + <p> Hızlı antijen testinizin sonucu pozitif çıkarsa test sonucunuzu Uygulama üzerinden açarsanız , diÄŸer insanları uyarabilirsiniz. PCR test sonucu pozitif çıktığında yakınlarınızı test sonucunu @@ -659,19 +666,20 @@ olabilir. </p> <h2> - e. Uygulamanın bilgilenme amaçlı kullanımı + f. Uygulamanın bilgilenme amaçlı kullanımı </h2> + <p> - Uygulama otomatik olarak sunucu sistemi üzerinden günlük istatistikleri - alır ve bunlar Uygulamada görüntülenir. Bu sırada eriÅŸim verileri oluÅŸur. - Uygulamada örneÄŸin <u>www.bundesregierung.de</u> gibi baÄŸlantılı web - siteleri açılır ve standart tarayıcıda (Android akıllı telefonlar) veya - Uygulamada (iPhone’lar) görüntülenir. Hangi verilerin iÅŸleneceÄŸi, eriÅŸilen - web sitesinin ilgili saÄŸlayıcısı tarafından belirlenmektedir. + Uygulama otomatik olarak sunucu sistemi üzerinden günlük istatistikleri alır ve bunlar + Uygulamada görüntülenir. Bu sırada eriÅŸim verileri oluÅŸur. Uygulamada örneÄŸin + www.bundesregierung.de gibi baÄŸlantılı web siteleri açılır ve standart tarayıcıda (Android + akıllı telefonlar) veya Uygulamada (iPhone’lar) görüntülenir. Hangi verilerin iÅŸleneceÄŸi, + eriÅŸilen web sitesinin ilgili saÄŸlayıcısı tarafından belirlenmektedir. </p> <h2> - f. Temas güncesi + g. Temas güncesi </h2> + <p> Temas güncesi, Uygulamanın ek bir iÅŸlevidir. Temas güncesindeki veriler, bir hatırlatma görevi görür ve yalnızca sizin tarafınızdan eriÅŸilebilir. Daha sonraki bir tarihte Korona testiniz @@ -686,8 +694,9 @@ enfeksiyonların önüne geçebilir. </p> <h2> - g. Veri bağışı + h. Veri bağışı </h2> + <p> Veri bağışı, Uygulamanın ek bir iÅŸlevidir. Veri bağışı kapsamında RKI’ye aktarılan kullanım verileri ve diÄŸer isteÄŸe baÄŸlı veriler, Uygulamanın etki gücünü ölçmek ve aÅŸağıda sıralanan @@ -727,16 +736,18 @@ öğrenmez. </p> <h2> - h. Anketler + i. Anketler </h2> + <p> Anketler, Uygulama dışında yönlendirileceÄŸiniz bir web sitesinde gerçekleÅŸtirilir. Uygulama, anketlerle baÄŸlantılı olarak RKI’ye veri aktarımı saÄŸlamaz. RKI tarafından yürütülen anketlerin amaçları, anket web sitesinde anketteki bilgilerde yer almaktadır. </p> <h2> - i. Uygulamanızın orijinalliÄŸinin doÄŸrulanması + j. Uygulamanızın orijinalliÄŸinin doÄŸrulanması </h2> + <p> Uygulamanızın orijinalliÄŸinin doÄŸrulanması için akıllı telefonunuzun iÅŸletim sisteminin bir iÅŸlevi kullanılır. Bu sayede sadece uygulaması düzgün çalışan Uygulama kullanıcılarının veri @@ -776,7 +787,7 @@ <p> Ä°lgili sunucu sistemi, aldığı pozitif listeleri kendi pozitif listesiyle birleÅŸtirir, bu sayede risk deÄŸerlendirmesinde, diÄŸer Korona uygulamalarının kullanıcılarıyla olan riskli karşılaÅŸmalar - da dikkate alınabilir (bkz. Madde 6 c.). DiÄŸer katılımcı ülkeler, RKI tarafından saÄŸlanan + da dikkate alınabilir (bkz. Madde 6 e.). DiÄŸer katılımcı ülkeler, RKI tarafından saÄŸlanan pozitif listelerle aynı yöntemi kullanarak çalışmaktadır. </p> <p> @@ -808,65 +819,62 @@ </li> </ul> <p> - Sisteme katılımcı ülkeler listesinin deÄŸiÅŸebileceÄŸini, lütfen göz önünde - bulundurun. Ä°lgili sorumlu saÄŸlık kurumları hakkında bilgilerin bulunduÄŸu - güncel listenin yer aldığı sıkça sorulan sorulara - https://www.coronawarn.app/de/faq/#interoperability_countries + Sisteme katılımcı ülkeler listesinin deÄŸiÅŸebileceÄŸini, lütfen göz önünde bulundurun. Ä°lgili + sorumlu saÄŸlık kurumları hakkında bilgilerin bulunduÄŸu güncel listenin yer aldığı sıkça sorulan + sorulara <a href="https://www.coronawarn.app/en/faq/#interoperability_countries">https://www.coronawarn.app/en/faq/#interoperability_countries</a> adresinden eriÅŸebilirsiniz. </p> <h1> 8. Uygulamanın hangi izinlere gerek duyar? </h1> <p> - Uygulama, akıllı telefonunuzdaki çeÅŸitli iÅŸlevlere ve arabirimlere eriÅŸim - gerektirir. Bunun için, Uygulamaya belli bazı izinleri vermenizi - gereklidir. Bu yetkilendirme sistemi, iÅŸletim sisteminizin teknik - özelliklerine göre deÄŸiÅŸmektedir. ÖrneÄŸin yetkilendirmeler, akıllı - telefonunuzda yetkilendirme kategorilerinde birleÅŸtirilmiÅŸ olabilir ve - sizin yetkilendirme kategorisini bir bütün olarak kabul etmeniz - gerekebilir. Uygulama tarafından talep edilen yetkiler verilmezse, hiçbir - Uygulama iÅŸlevinin kullanılamayacağını veya yalnızca birkaçının + Uygulama, akıllı telefonunuzdaki çeÅŸitli iÅŸlevlere ve arabirimlere eriÅŸim gerektirir. Bunun + için, Uygulamaya belli bazı izinleri vermenizi gereklidir. Bu yetkilendirme sistemi, iÅŸletim + sisteminizin teknik özelliklerine göre deÄŸiÅŸmektedir. ÖrneÄŸin yetkilendirmeler, akıllı + telefonunuzda yetkilendirme kategorilerinde birleÅŸtirilmiÅŸ olabilir ve sizin yetkilendirme + kategorisini bir bütün olarak kabul etmeniz gerekebilir. Uygulama tarafından talep edilen + yetkiler verilmezse, hiçbir Uygulama iÅŸlevinin kullanılamayacağını veya yalnızca birkaçının kullanılabileceÄŸini lütfen unutmayın. </p> <h2> a. Teknik gereksinimler (tüm akıllı telefonlar) </h2> + <ul> <li> - Uygulama, sunucu sistemiyle veri alışveriÅŸinde bulunabilmek için - internet baÄŸlantısı gerektirir. + Uygulama, sunucu sistemiyle veri alışveriÅŸinde bulunabilmek için internet baÄŸlantısı + gerektirir. </li> <li> - Akıllı telefonunuzun kendi rastgele kimlik numaralarını gönderebilmesi - ve diÄŸer akıllı telefonlardan rastgele kimlik numaralarını - kaydedebilmesi için, Bluetooth iÅŸlevinin etkinleÅŸtirilmesi zorunludur. + Akıllı telefonunuzun kendi rastgele kimlik numaralarını gönderebilmesi ve diÄŸer akıllı + telefonlardan rastgele kimlik numaralarını kaydedebilmesi için, Bluetooth iÅŸlevinin + etkinleÅŸtirilmesi zorunludur. </li> <li> - Uygulama, enfeksiyon riskinizi otomatik olarak hesaplamak ve bir - kayıtlı testin durumunu sorgulamak için akıllı telefonunuzda arka - planda çalışabilmelidir. Arka plan iÅŸletimini devre dışı bırakırsanız, - Uygulamanın tüm eylemlerini kendiniz baÅŸlatmanız gerekir. + Uygulama, enfeksiyon riskinizi otomatik olarak hesaplamak ve bir kayıtlı testin durumunu + sorgulamak için akıllı telefonunuzda arka planda çalışabilmelidir. Arka plan iÅŸletimini + devre dışı bırakırsanız, Uygulamanın tüm eylemlerini kendiniz baÅŸlatmanız gerekir. </li> </ul> <h2> b. Android akıllı telefonları </h2> + <p> - Bir Android akıllı telefon kullanıyorsanız, ayrıca aÅŸağıdaki sistem - iÅŸlevlerinin de etkinleÅŸtirilmesi gerekir: + Bir Android akıllı telefon kullanıyorsanız, ayrıca aÅŸağıdaki sistem iÅŸlevlerinin de + etkinleÅŸtirilmesi gerekir: </p> <ul> <li> Android’in COVID-19 bildirim sistemi (COVID-19 temas bildirimleri) </li> <li> - Sürüm 10’a kadar olan Android sürümlerinde konum belirleme özelliÄŸi - etkinleÅŸtirilmelidir. Ancak konum verileri toplanmaz. + Sürüm 10’a kadar olan Android sürümlerinde konum belirleme özelliÄŸi etkinleÅŸtirilmelidir. + Ancak konum verileri toplanmaz. </li> <li> - Enfeksiyon riskinizin ve test sonuçlarının durumlarındaki - deÄŸiÅŸikliklerden haberdar olmak için, bildirim iÅŸlevinin - etkinleÅŸtirilmiÅŸ olmalıdır. Bildirim iÅŸlevi, iÅŸletim sisteminde + Enfeksiyon riskinizin ve test sonuçlarının durumlarındaki deÄŸiÅŸikliklerden haberdar olmak + için, bildirim iÅŸlevinin etkinleÅŸtirilmiÅŸ olmalıdır. Bildirim iÅŸlevi, iÅŸletim sisteminde standart olarak etkinleÅŸtirilmiÅŸtir. </li> </ul> @@ -882,17 +890,17 @@ <h2> c. iPhone’lar (Apple iOS) </h2> + <p> - Bir iPhone kullanıyorsanız, aÅŸağıdaki sistem iÅŸlevlerinin de - etkinleÅŸtirilmesi gerekir: + Bir iPhone kullanıyorsanız, aÅŸağıdaki sistem iÅŸlevlerinin de etkinleÅŸtirilmesi gerekir: </p> <ul> <li> iOS’in COVID-19 bildirim sistemi (maruz kalma bildirimleri) </li> <li> - Enfeksiyon riskinizin ve test sonucunuzun durumundaki deÄŸiÅŸikliklerden - haberdar olmak için, bildirimler iÅŸlevi etkinleÅŸtirilmiÅŸ olmalıdır. + Enfeksiyon riskinizin ve test sonucunuzun durumundaki deÄŸiÅŸikliklerden haberdar olmak için, + bildirimler iÅŸlevi etkinleÅŸtirilmiÅŸ olmalıdır. </li> </ul> <p> @@ -908,17 +916,17 @@ 9. Verileriniz ne zaman silinir? </h1> <p> - Saklama süresi, verilerinizin kaydedildiÄŸi amaçlara veya Uygulama - iÅŸlevlerine göre deÄŸiÅŸmektedir. Verilerin saklama süresini belirlerken RKI, - 14 güne kadar süren inkübasyon dönemi (hastalığın bulaÅŸmasından hastalığın - baÅŸlangıcına kadar geçen süre) ve enfekte bir kiÅŸinin inkübasyon dönemi - bitiminden sonra diÄŸer insanlar için oluÅŸturduÄŸu enfeksiyon riskinin - süresine iliÅŸkin güncel bilimsel bulguları dikkate alır. Madde 6’da daha - kısa bir saklama süresi belirtilmedikçe, aÅŸağıdaki süreler geçerlidir: + Saklama süresi, verilerinizin kaydedildiÄŸi amaçlara veya Uygulama iÅŸlevlerine göre + deÄŸiÅŸmektedir. Verilerin saklama süresini belirlerken RKI, 14 güne kadar süren inkübasyon dönemi + (hastalığın bulaÅŸmasından hastalığın baÅŸlangıcına kadar geçen süre) ve enfekte bir kiÅŸinin + inkübasyon dönemi bitiminden sonra diÄŸer insanlar için oluÅŸturduÄŸu enfeksiyon riskinin süresine + iliÅŸkin güncel bilimsel bulguları dikkate alır. Madde 6’da daha kısa bir saklama süresi + belirtilmedikçe, aÅŸağıdaki süreler geçerlidir: </p> <h2> a. Akıllı telefonunuzdaki veriler </h2> + <p> Pozitif listeler 14 gün geçtikten sonra Uygulama belleÄŸinden silinir. “GiriÅŸ denetimlerim†alanındaki olay verileri 14 gün sonra otomatik olarak silinecektir. Alternatif olarak, “GiriÅŸ @@ -929,24 +937,28 @@ günlüğündeki veriler akıllı telefonunuzda 16 gün boyunca saklanır ve ardından otomatik olarak silinir. Ancak bu verileri istediÄŸiniz zaman, daha erken de silebilirsiniz. Bir olaya veya bir konuma giriÅŸ denetimi yaptırdığınızda, ilgili giriÅŸ denetimi kaydı silindikten sonra bile temas - günlüğündeki verilerin hâlâ orada saklı kaldıklarını lütfen unutmayın. + günlüğündeki verilerin hâlâ orada saklı kaldıklarını lütfen unutmayın. Bir kez hızlı test + profili oluÅŸturduysanız bu, siz tekrar silene kadar uygulamada kalır. </p> <h2> b. Sunucu sistemlerindeki veriler </h2> + <p> - Pozitif listeler, 14 gün sonra tüm sunucu sistemlerinden (deÄŸiÅŸim - sunucuları dahil) silinir. DiÄŸer tüm veriler en geç 21 gün sonra silinir. + Pozitif listeler, 14 gün sonra tüm sunucu sistemlerinden (deÄŸiÅŸim sunucuları dahil) silinir. + DiÄŸer tüm veriler en geç 21 gün sonra silinir. </p> <h2> c. Veri bağışı </h2> + <p> Veri bağışı kapsamında RKI’ye aktarılan kullanım verileri ve diÄŸer isteÄŸe baÄŸlı veriler 180 gün sonra silinir.</p> <h2> d. Uygulamanızın orijinalliÄŸinin doÄŸrulanması </h2> + <p> Uygulamanızın orijinalliÄŸini doÄŸrulamak için akıllı telefonunuzun oluÅŸturduÄŸu kimlik kodları, RKI’ye gönderildikten 30 gün sonra sunucu sistemlerinden silinir. @@ -964,8 +976,8 @@ kullanıcılarına olay kimlikleri yalnızca RKI sunucu sistemi aracılığıyla dağıtılır. Pozitif antijen hızlı testi nedeniyle uyarı durumunda verileriniz deÄŸiÅŸim sunucusuna aktarılmaz. - </p> + <p> Katılımcı AB ülkelerinin yetkili ulusal saÄŸlık kurumları, ortaklaÅŸa iÅŸletilen uyarı sisteminin iÅŸletimi ve bakımı için AB Komisyonuna iÅŸletici kuruluÅŸ olarak yetki vermiÅŸtir. Corona-Warn-App @@ -973,6 +985,7 @@ sunucusu, RKI ile koordineli olarak Ä°sviçre Federal SaÄŸlık Dairesi tarafından iÅŸletilir ve bakımı yapılır. </p> + <p> RKI, Uygulamanın teknik altyapısının bir bölümünün (örn. sunucu sistemleri, yardım hattı) iÅŸletimi ve bakımı için, iÅŸletici kuruluÅŸ olarak T-Systems International GmbH ve SAP Deutschland @@ -1018,34 +1031,32 @@ 12. VerdiÄŸiniz rıza beyanını nasıl geri alabilirsiniz? </h1> <p> - GeleceÄŸe dönük geçerli olmak üzere, Uygulamada RKI’ye verdiÄŸiniz bir onayı - istediÄŸiniz zaman geri alma hakkına sahipsiniz. Bununla birlikte, - verileriniz iÅŸlenmesi halen gerçekleÅŸtirilmiÅŸ ise artık bu iÅŸlemler geri - alınamaz. RKI’nin, özellikle akıllı telefonlardan diÄŸer kullanıcılara halen - aktarılmış olan rastgele kimlik numaralarını silme olanağı yoktur. + GeleceÄŸe dönük geçerli olmak üzere, Uygulamada RKI’ye verdiÄŸiniz bir onayı istediÄŸiniz zaman + geri alma hakkına sahipsiniz. Bununla birlikte, verileriniz iÅŸlenmesi halen gerçekleÅŸtirilmiÅŸ + ise artık bu iÅŸlemler geri alınamaz. RKI’nin, özellikle akıllı telefonlardan diÄŸer kullanıcılara + halen aktarılmış olan rastgele kimlik numaralarını silme olanağı yoktur. </p> <h2> a. Maruz kalma güncesine iliÅŸkin rıza beyanı </h2> + <p> - Maruz kalma güncesine vermiÅŸ olduÄŸunuz rıza beyanını, istediÄŸiniz zaman - Uygulamadaki kaydırıcıyı kullanarak, iÅŸlevi devre dışı bırakabilir veya - Uygulamayı silerek iptal edebilirsiniz. Risk deÄŸerlendirmesini tekrar - kullanmak isterseniz, kaydırıcıyı yeniden etkinleÅŸtirebilir veya Uygulamayı - yeniden yükleyebilirsiniz. + Maruz kalma güncesine vermiÅŸ olduÄŸunuz rıza beyanını, istediÄŸiniz zaman Uygulamadaki kaydırıcıyı + kullanarak, iÅŸlevi devre dışı bırakabilir veya Uygulamayı silerek iptal edebilirsiniz. Risk + deÄŸerlendirmesini tekrar kullanmak isterseniz, kaydırıcıyı yeniden etkinleÅŸtirebilir veya + Uygulamayı yeniden yükleyebilirsiniz. </p> <h2> b. “Test sonucunu çağırın†iÅŸlevine dair rıza beyanı </h2> + <p> - Uygulamanın test sonucunu çağırması için verdiÄŸiniz rıza beyanını, testi - Uygulamada görüntüleyip, sonra kaldırarak iptal edebilirsiniz. Bunun - ardından test sonucunu çağırmak için kullanılan belirteç, Uygulama - belleÄŸinden silinir, böylece belirteç artık sunucu sisteminde atanamaz. - Aynı testi Uygulamanıza yeniden atamanız veya aynı QR kodu yeniden - taramanız mümkün deÄŸildir. Yeniden bir test yaptırırsanız ve test sonucunu - görmek isterseniz, yeniden buna dair rıza beyanında bulunmanız - istenecektir. Ancak test sonucu Uygulamada halen mevcutsa, ilgili rıza + Uygulamanın test sonucunu çağırması için verdiÄŸiniz rıza beyanını, testi Uygulamada + görüntüleyip, sonra kaldırarak iptal edebilirsiniz. Bunun ardından test sonucunu çağırmak için + kullanılan belirteç, Uygulama belleÄŸinden silinir, böylece belirteç artık sunucu sisteminde + atanamaz. Aynı testi Uygulamanıza yeniden atamanız veya aynı QR kodu yeniden taramanız mümkün + deÄŸildir. Yeniden bir test yaptırırsanız ve test sonucunu görmek isterseniz, yeniden buna dair + rıza beyanında bulunmanız istenecektir. Ancak test sonucu Uygulamada halen mevcutsa, ilgili rıza beyanı artık geri alınamaz. </p> <h2> @@ -1061,6 +1072,7 @@ olanak, diÄŸer kullanıcıları uyarmak için henüz rastgele kimlik numaralarınızı göndermediÄŸiniz sürece söz konusudur. </p> + <p> Rastgele kimlik numaralarınızı gönderdikten sonra, verdiÄŸiniz rıza beyanını geri almanın tek yolu artık Uygulamayı silmektir. Sunucu sistemine halen iletilmiÅŸ olan rastgele kimlik @@ -1093,6 +1105,7 @@ <h2> e. “Veri bağışı†rıza beyanı </h2> + <p> Uygulamanın ayarlarından “Verileri bağışla†seçeneÄŸini devre dışı bırakarak, istediÄŸiniz zaman vermiÅŸ olduÄŸunuz rızayı geri alabilirsiniz. Bunun ardından Uygulama, artık kullanım verilerinizi @@ -1102,6 +1115,7 @@ <h2> f. “Anket†rıza beyanı </h2> + <p> Bir RKI anketine katılmak için gereken rızanızı, Uygulamada deÄŸil, anketin gerçekleÅŸtirildiÄŸi web sitesi üzerinden verirsiniz. Orada, verdiÄŸiniz rızayı nasıl geri alabileceÄŸinize iliÅŸkin bir @@ -1110,6 +1124,7 @@ <h2> g. “Uygulamanızın orijinalliÄŸinin doÄŸrulanması†rıza beyanı </h2> + <p> Uygulamanızın orijinalliÄŸinin doÄŸrulanması için verdiÄŸiniz rızayı geri alırsanız, bunun ilgili veri iÅŸleme üzerinde doÄŸrudan herhangi bir etkisi yoktur. Siz bu rızanızı verdikten hemen sonra, @@ -1120,54 +1135,50 @@ 13. BaÅŸka hangi veri koruma haklarına sahipsiniz? </h1> <p> - KiÅŸisel verilerinizi RKI tarafından iÅŸlendiÄŸi sürece, ek olarak aÅŸağıdaki - veri koruma haklarına da sahipsiniz: + KiÅŸisel verilerinizi RKI tarafından iÅŸlendiÄŸi sürece, ek olarak aÅŸağıdaki veri koruma haklarına + da sahipsiniz: </p> <ul> <li> GVKT madde 15, 16, 17, 18, 20 ve 21 kapsamındaki haklar, </li> <li> - RKI’nin veri koruma görevlisi - ile iletiÅŸim geçme ve isteklerinizi dile getirme hakkı (GVKT madde 38, - fıkra 4 uyarınca) ve + <a href="https://www.rki.de/DE/Content/Institut/OrgEinheiten/Datenschutz/Datenschutz_node.html"> + RKI’nin veri koruma görevlisi + </a> + ile iletiÅŸim geçme ve isteklerinizi dile getirme hakkı (GVKT madde 38, fıkra 4 uyarınca) ve </li> <li> - veri koruma denetim makamına ÅŸikayette bulunma hakkı. Bunun için ya - ikâmet yerinizdeki yetkili denetim makamı ya da RKI için yetkili makam - ile iletiÅŸime geçebilirsiniz. RKI için yetkili denetim makamı: - Bundesbeauftragte für den Datenschutz und die Informationsfreiheit - (Federal Veri Koruma ve Bilgi Özgürlüğü Komiseri) Graurheindorfer Str. - 153, 53117 Bonn. + veri koruma denetim makamına ÅŸikayette bulunma hakkı. Bunun için ya ikâmet yerinizdeki + yetkili denetim makamı ya da RKI için yetkili makam ile iletiÅŸime geçebilirsiniz. RKI için + yetkili denetim makamı: Bundesbeauftragte für den Datenschutz und die Informationsfreiheit + (Federal Veri Koruma ve Bilgi Özgürlüğü Komiseri) Graurheindorfer Str. 153, 53117 Bonn. </li> </ul> <p> - Bu veri koruma hakları, diÄŸer insanları uyarmak için son birkaç güne ait - rastgele kimliklerinizi gönderdiÄŸiniz sürece (Madde 7’ye bakın), veri - deÄŸiÅŸim sunucusuna katılan ülkelerdeki veri iÅŸlemeden sorumlu saÄŸlık - kurumları tarafından da size tanınmaktadır. -</p> -<p> - Lütfen yukarıda sözü geçen veri koruma haklarının, yalnızca bu talep edilen - hakların ilgili olduÄŸu verilerin açıkça sizin ÅŸahsınıza atanabilmesi - halinde ifa edilebileceÄŸini göz önünde bulundurun. Bu yalnızca, sunucu - sistemine aktarılan verilerin, ÅŸahsınıza veya akıllı telefonunuza açık bir - ÅŸekilde atanmasını saÄŸlayan Uygulama üzerinden daha fazla kiÅŸisel verilerin - toplanmasıyla mümkün olabilmektedir. Bu iÅŸlem Uygulamanın amaçları - doÄŸrultusunda gerekli olmadığından, RKI bu tür ek verileri toplamak zorunda - deÄŸildir (GVKT madde 11, fıkra 2). Kaldı ki, bu iÅŸlem, mümkün olduÄŸunca az - veri toplama hedefine ters düşecektir. Bu nedenle, yukarıda belirtilen veri - koruma hakları, kimliÄŸiniz hakkında vermiÅŸ olduÄŸunuz ek bilgilerle bile + Bu veri koruma hakları, diÄŸer insanları uyarmak için son birkaç güne ait rastgele kimliklerinizi + gönderdiÄŸiniz sürece (Madde 7’ye bakın), veri deÄŸiÅŸim sunucusuna katılan ülkelerdeki veri + iÅŸlemeden sorumlu saÄŸlık kurumları tarafından da size tanınmaktadır. +</p> + +<p> + Lütfen yukarıda sözü geçen veri koruma haklarının, yalnızca bu talep edilen hakların ilgili + olduÄŸu verilerin açıkça sizin ÅŸahsınıza atanabilmesi halinde ifa edilebileceÄŸini göz önünde + bulundurun. Bu yalnızca, sunucu sistemine aktarılan verilerin, ÅŸahsınıza veya akıllı + telefonunuza açık bir ÅŸekilde atanmasını saÄŸlayan Uygulama üzerinden daha fazla kiÅŸisel + verilerin toplanmasıyla mümkün olabilmektedir. Bu iÅŸlem Uygulamanın amaçları doÄŸrultusunda + gerekli olmadığından, RKI bu tür ek verileri toplamak zorunda deÄŸildir (GVKT madde 11, fıkra 2). + Kaldı ki, bu iÅŸlem, mümkün olduÄŸunca az veri toplama hedefine ters düşecektir. Bu nedenle, + yukarıda belirtilen veri koruma hakları, kimliÄŸiniz hakkında vermiÅŸ olduÄŸunuz ek bilgilerle bile genellikle yerine getirilememektedir. </p> <h1> 14. Veri koruma görevlisi ve iletiÅŸim </h1> <p> - Veri gizliliÄŸine iliÅŸkin sorularınızı ve endiÅŸelerinizi RKI’nin resmi veri - koruma görevlisine gönderebilirsiniz: Robert Koch-Institut, z. H. des - Datenschutzbeauftragten (veri koruma görevlisi), Nordufer 20,13353 Berlin - veya e-posta yoluyla: datenschutz@rki.de. + Veri gizliliÄŸine iliÅŸkin sorularınızı ve endiÅŸelerinizi RKI’nin resmi veri koruma görevlisine + gönderebilirsiniz: Robert Koch-Institut, z. H. des Datenschutzbeauftragten (veri koruma + görevlisi), Nordufer 20,13353 Berlin veya e-posta yoluyla: <a href="mailto:datenschutz@rki.de">datenschutz@rki.de</a>. </p> <p> Baskı 30.04.2021 diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt index 3767a49eac79fa2be3673d031c3b62f97670501a..7adcb05ae735cdca27b6cbf15f7c90be8a371b80 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt @@ -33,11 +33,13 @@ import de.rki.coronawarnapp.risk.RiskLevelChangeDetector import de.rki.coronawarnapp.risk.execution.ExposureWindowRiskWorkScheduler import de.rki.coronawarnapp.submission.auto.AutoSubmission import de.rki.coronawarnapp.task.TaskController +import de.rki.coronawarnapp.util.BuildVersionWrap import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.WatchdogService import de.rki.coronawarnapp.util.device.ForegroundState import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.ApplicationComponent +import de.rki.coronawarnapp.util.hasAPILevel import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -85,8 +87,10 @@ class CoronaWarnApplication : Application(), HasAndroidInjector { CWADebug.init(this) AppInjector.init(this).let { compPreview -> - Timber.v("Calling EncryptedPreferencesMigration.doMigration()") - compPreview.encryptedMigration.doMigration() + if (BuildVersionWrap.hasAPILevel(23)) { + Timber.v("Calling EncryptedPreferencesMigration.doMigration()") + compPreview.encryptedMigration.get().doMigration() + } CWADebug.initAfterInjection(compPreview) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt index a090b59fadb6860df156d971cc1056565ec12163..638bc4082866a671d4fa8b1222f3da2111faf573 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/BugReportingSharedModule.kt @@ -5,12 +5,17 @@ import dagger.Provides import dagger.Reusable import dagger.multibindings.IntoSet import de.rki.coronawarnapp.bugreporting.censors.BugCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryEncounterCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryLocationCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryPersonCensor -import de.rki.coronawarnapp.bugreporting.censors.DiaryVisitCensor -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor -import de.rki.coronawarnapp.bugreporting.censors.RegistrationTokenCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryEncounterCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryLocationCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryPersonCensor +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryVisitCensor +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.CheckInsCensor +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RACoronaTestCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RatProfileCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebugLoggerScope import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope import de.rki.coronawarnapp.bugreporting.debuglog.upload.server.LogUploadApiV1 @@ -71,11 +76,19 @@ class BugReportingSharedModule { @Provides @IntoSet - fun registrationTokenCensor(censor: RegistrationTokenCensor): BugCensor = censor + fun registrationTokenCensor(censor: CoronaTestCensor): BugCensor = censor @Provides @IntoSet - fun qrCodeCensor(censor: QRCodeCensor): BugCensor = censor + fun pcrQrCodeCensor(censor: PcrQrCodeCensor): BugCensor = censor + + @Provides + @IntoSet + fun ratQrCodeCensor(censor: RatQrCodeCensor): BugCensor = censor + + @Provides + @IntoSet + fun raCoronaTestCensor(censor: RACoronaTestCensor): BugCensor = censor @Provides @IntoSet @@ -92,4 +105,16 @@ class BugReportingSharedModule { @Provides @IntoSet fun diaryVisitCensor(censor: DiaryVisitCensor): BugCensor = censor + + @Provides + @IntoSet + fun checkInsCensor(censor: CheckInsCensor): BugCensor = censor + + @Provides + @IntoSet + fun traceLocationsCensor(censor: TraceLocationCensor): BugCensor = censor + + @Provides + @IntoSet + fun ratProfileCensor(censor: RatProfileCensor): BugCensor = censor } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt index 8be45d08b70e1fcf0dec7a11becd81b00a5cafaa..48c13a4a73e9de887be0621f210e447c91dd8759 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/BugCensor.kt @@ -38,6 +38,34 @@ interface BugCensor { return true } + fun withValidDescription(description: String?, action: (String) -> Unit): Boolean { + if (description.isNullOrBlank()) return false + if (description.length < 5) return false + action(description) + return true + } + + fun withValidAddress(address: String?, action: (String) -> Unit): Boolean { + if (address.isNullOrBlank()) return false + if (address.length < 4) return false + action(address) + return true + } + + fun withValidCity(city: String?, action: (String) -> Unit): Boolean { + if (city.isNullOrBlank()) return false + if (city.length < 3) return false + action(city) + return true + } + + fun withValidZipCode(zipCode: String?, action: (String) -> Unit): Boolean { + if (zipCode.isNullOrBlank()) return false + if (zipCode.length < 5) return false + action(zipCode) + return true + } + fun LogLine.toNewLogLineIfDifferent(newMessage: String): LogLine? { return if (newMessage != message) copy(message = newMessage) else null } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt index b8d3934117675dc62e9e8d29f607b42fcb172f36..9c75a723f8df9d5ff4cc8caa8c6efd13e75ecb3b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryEncounterCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidComment import de.rki.coronawarnapp.bugreporting.debuglog.LogLine diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt similarity index 94% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt index 24bf031cf63dff35797f0a0049b3fd133ce2ca8a..70628444cbaa4ed279be2e03adc54bb37bbc6c1e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryLocationCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidEmail import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt similarity index 94% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt index 42341a8b5fd687da53a5e63ed7e93ab99477f28c..a8a0056d95bc5027c1f79e3d4d3e0451cd0b41a7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryPersonCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidEmail import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt index ff0c63cf1100805d6a36e6ce74efff917445e4cc..f060c0fd2d49e2e415664e3dac90ad56937bbef5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/contactdiary/DiaryVisitCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.contactdiary import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..a5f10d81cb24ca19dbf32068e6e878b557ac84e0 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/CheckInsCensor.kt @@ -0,0 +1,55 @@ +package de.rki.coronawarnapp.bugreporting.censors.presencetracing + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidDescription +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +@Reusable +class CheckInsCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val checkInRepository: CheckInRepository +) : BugCensor { + + private val checkInsFlow by lazy { + checkInRepository.allCheckIns.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val checkIns = checkInsFlow.first() + + if (checkIns.isEmpty()) return null + + val newLogMsg = checkIns.fold(entry.message) { initial, checkIn -> + + var acc = initial + + withValidDescription(checkIn.description) { description -> + acc = acc.replace(description, "CheckIn#${checkIn.id}/Description") + } + + withValidAddress(checkIn.address) { address -> + acc = acc.replace(address, "CheckIn#${checkIn.id}/Address") + } + + acc + } + + return entry.toNewLogLineIfDifferent(newLogMsg) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..6d3f60ffb8bf2bc8dc61612b9722a9fc67fac64b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/presencetracing/TraceLocationCensor.kt @@ -0,0 +1,80 @@ +package de.rki.coronawarnapp.bugreporting.censors.presencetracing + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidDescription +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.presencetracing.locations.TraceLocationUserInput +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +/** + * Censors Trace Location Data + * + * The information about which data to censor comes from two places + * - traceLocationRepository, for traceLocations that are already stored + * - dataToCensor, which is set before a traceLocation is created; this is needed in cases when the app crashes between + * data input and storing + */ +@Reusable +class TraceLocationCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val traceLocationRepository: TraceLocationRepository +) : BugCensor { + + private val traceLocationsFlow by lazy { + traceLocationRepository.allTraceLocations.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val traceLocations = traceLocationsFlow.first() + + var newLogMsg = traceLocations.fold(entry.message) { initial, traceLocation -> + var acc = initial + + acc = acc.replace(traceLocation.type.name, "TraceLocation#${traceLocation.id}/Type") + + withValidDescription(traceLocation.description) { description -> + acc = acc.replace(description, "TraceLocation#${traceLocation.id}/Description") + } + + withValidAddress(traceLocation.address) { address -> + acc = acc.replace(address, "TraceLocation#${traceLocation.id}/Address") + } + + acc + } + + val inputDataToCensor = dataToCensor + if (inputDataToCensor != null) { + newLogMsg = newLogMsg.replace(inputDataToCensor.type.name, "TraceLocationUserInput#Type") + + withValidDescription(inputDataToCensor.description) { + newLogMsg = newLogMsg.replace(inputDataToCensor.description, "TraceLocationUserInput#Description") + } + + withValidAddress(inputDataToCensor.address) { + newLogMsg = newLogMsg.replace(inputDataToCensor.address, "TraceLocationUserInput#Address") + } + } + + return entry.toNewLogLineIfDifferent(newLogMsg) + } + + companion object { + var dataToCensor: TraceLocationUserInput? = null + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt similarity index 56% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt index c8769d23087ff67a5d92401421161273d206e739..df934b7e678642cc9e1eff255b67473bfe70af25 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/CoronaTestCensor.kt @@ -1,6 +1,7 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.submission import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.coronatest.CoronaTestRepository @@ -9,17 +10,25 @@ import kotlinx.coroutines.flow.first import javax.inject.Inject @Reusable -class RegistrationTokenCensor @Inject constructor( +class CoronaTestCensor @Inject constructor( private val coronaTestRepository: CoronaTestRepository, ) : BugCensor { + + // Keep a history to have references even after the user deletes a test + private val tokenHistory = mutableSetOf<String>() + private val identifierHistory = mutableSetOf<String>() + override suspend fun checkLog(entry: LogLine): LogLine? { + + // The Registration Token is received after registration of PCR and RAT tests. It is required to poll the test result. val tokens = coronaTestRepository.coronaTests.first().map { it.registrationToken } + tokenHistory.addAll(tokens) - if (tokens.isEmpty()) return null + val identifiers = coronaTestRepository.coronaTests.first().map { it.identifier } + identifierHistory.addAll(identifiers) var newMessage = entry.message - - for (token in tokens) { + for (token in tokenHistory) { if (!entry.message.contains(token)) continue newMessage = if (CWADebug.isDeviceForTestersBuild) { @@ -29,6 +38,12 @@ class RegistrationTokenCensor @Inject constructor( } } + identifierHistory + .filter { entry.message.contains(it) } + .forEach { + newMessage = newMessage.replace(it, "${it.take(11)}CoronaTest/Identifier") + } + return entry.toNewLogLineIfDifferent(newMessage) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt similarity index 83% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt index b85888d1d02ba6fb39e379e7740c1fb68aefe39a..3880817e6806f408469f061cf7b2a2f682aab1c0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/PcrQrCodeCensor.kt @@ -1,13 +1,14 @@ -package de.rki.coronawarnapp.bugreporting.censors +package de.rki.coronawarnapp.bugreporting.censors.submission import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.util.CWADebug import javax.inject.Inject @Reusable -class QRCodeCensor @Inject constructor() : BugCensor { +class PcrQrCodeCensor @Inject constructor() : BugCensor { override suspend fun checkLog(entry: LogLine): LogLine? { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..38c06dca4ae6ed269f293e30b47948baeae28467 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RACoronaTestCensor.kt @@ -0,0 +1,57 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebuggerScope +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +class RACoronaTestCensor @Inject constructor( + @DebuggerScope debugScope: CoroutineScope, + private val coronaTestRepository: CoronaTestRepository +) : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + private val coronaTestFlow by lazy { + coronaTestRepository.coronaTests.stateIn( + scope = debugScope, + started = SharingStarted.Lazily, + initialValue = null + ).filterNotNull() + } + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val raCoronaTestFlow = coronaTestFlow.map { tests -> tests.filterIsInstance<RACoronaTest>() }.first() + val raCoronaTest = raCoronaTestFlow.firstOrNull() ?: return null + + var newMessage = entry.message + + with(raCoronaTest) { + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RATest/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RATest/LastName") + } + + val dateOfBirthString = dateOfBirth?.toString(dayOfBirthFormatter) ?: return@with + + newMessage = newMessage.replace(dateOfBirthString, "RATest/DateOfBirth") + } + + return entry.toNewLogLineIfDifferent(newMessage) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..fa1e919bbeeccadfded6038b795001cee462cf34 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensor.kt @@ -0,0 +1,74 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidAddress +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidCity +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidPhoneNumber +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidZipCode +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import kotlinx.coroutines.flow.first +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +class RatProfileCensor @Inject constructor( + private val ratProfileSettings: RATProfileSettings +) : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + private val ratProfileHistory = mutableSetOf<RATProfile>() + + override suspend fun checkLog(entry: LogLine): LogLine? { + val ratProfile = ratProfileSettings.profile.flow.first() + + // store the profile in a property so we still have a reference after it was deleted by the user + if (ratProfile != null) { + ratProfileHistory.add(ratProfile) + } + + var newMessage = entry.message + + ratProfileHistory.forEach { profile -> + with(profile) { + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RAT-Profile/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RAT-Profile/LastName") + } + + val dateOfBirthString = birthDate?.toString(dayOfBirthFormatter) + + if (dateOfBirthString != null) { + newMessage = newMessage.replace(dateOfBirthString, "RAT-Profile/DateOfBirth") + } + + withValidAddress(street) { street -> + newMessage = newMessage.replace(street, "RAT-Profile/Street") + } + + withValidCity(city) { city -> + newMessage = newMessage.replace(city, "RAT-Profile/City") + } + + withValidZipCode(zipCode) { zipCode -> + newMessage = newMessage.replace(zipCode, "RAT-Profile/Zip-Code") + } + + withValidPhoneNumber(phone) { phone -> + newMessage = newMessage.replace(phone, "RAT-Profile/Phone") + } + + withValidPhoneNumber(email) { phone -> + newMessage = newMessage.replace(phone, "RAT-Profile/eMail") + } + } + } + + return entry.toNewLogLineIfDifferent(newMessage) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt new file mode 100644 index 0000000000000000000000000000000000000000..8fe3c62f3d18c6b0c0600fe71f000c0f8090b831 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatQrCodeCensor.kt @@ -0,0 +1,63 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.censors.BugCensor +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.toNewLogLineIfDifferent +import de.rki.coronawarnapp.bugreporting.censors.BugCensor.Companion.withValidName +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.qrcode.RapidAntigenHash +import de.rki.coronawarnapp.util.CWADebug +import org.joda.time.LocalDate +import org.joda.time.format.DateTimeFormat +import javax.inject.Inject + +@Reusable +class RatQrCodeCensor @Inject constructor() : BugCensor { + + private val dayOfBirthFormatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + override suspend fun checkLog(entry: LogLine): LogLine? { + + val dataToCensor = dataToCensor ?: return null + + var newMessage = entry.message + + with(dataToCensor) { + newMessage = newMessage.replace(rawString, "RatQrCode/ScannedRawString") + + newMessage = if (CWADebug.isDeviceForTestersBuild) { + newMessage.replace(hash, PLACEHOLDER + hash.takeLast(28)) + } else { + newMessage.replace(hash, PLACEHOLDER + hash.takeLast(4)) + } + + withValidName(firstName) { firstName -> + newMessage = newMessage.replace(firstName, "RATest/FirstName") + } + + withValidName(lastName) { lastName -> + newMessage = newMessage.replace(lastName, "RATest/LastName") + } + + val dateOfBirthString = dateOfBirth?.toString(dayOfBirthFormatter) ?: return@with + + newMessage = newMessage.replace(dateOfBirthString, "RATest/DateOfBirth") + } + + return entry.toNewLogLineIfDifferent(newMessage) + } + + companion object { + var dataToCensor: CensorData? = null + + private const val PLACEHOLDER = "SHA256HASH-ENDING-WITH-" + } + + data class CensorData( + val rawString: String, + val hash: RapidAntigenHash, + val firstName: String?, + val lastName: String?, + val dateOfBirth: LocalDate? + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/ui/ErrorDialog.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/ui/ErrorDialog.kt index 49e16cef4548fbb8969147575de3e0042c78dd89..c4b4af463c590e0c4812a4e0d34e176197f837f1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/ui/ErrorDialog.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/ui/ErrorDialog.kt @@ -6,6 +6,7 @@ import android.text.method.LinkMovementMethod import android.text.util.Linkify import android.widget.TextView import androidx.appcompat.app.AlertDialog +import androidx.core.widget.TextViewCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder import de.rki.coronawarnapp.R import de.rki.coronawarnapp.util.ContextExtensions.getColorStateListCompat @@ -34,7 +35,7 @@ private fun MaterialAlertDialogBuilder.setMessageView( paddingStartEnd, paddingLeftRight ) - setTextAppearance(R.style.body1) + TextViewCompat.setTextAppearance(this, R.style.body1) setLinkTextColor(context.getColorStateListCompat(R.color.button_primary)) setTextIsSelectable(!textHasLinks) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt index 01cc23cd02002644647a976484e5c4e289dd6f1c..ee2d4f701b00c6e3632b1b2ebe075221efeb649d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.contactdiary.ui.day.tabs.location import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent +import androidx.core.widget.TextViewCompat import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.util.hideKeyboard import de.rki.coronawarnapp.contactdiary.util.setClickLabel @@ -48,10 +49,10 @@ class DiaryLocationViewHolder( if (duration == null || duration.millis == 0L) { text = context.getString(R.string.duration_dialog_default_value) setBackgroundResource(R.drawable.contact_diary_duration_background_default) - setTextAppearance(R.style.bodyNeutral) + TextViewCompat.setTextAppearance(this, R.style.bodyNeutral) } else { setBackgroundResource(R.drawable.contact_diary_duration_background_selected) - setTextAppearance(R.style.body1) + TextViewCompat.setTextAppearance(this, R.style.body1) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt index 74dff70083d075cca4827a6ef246a94810b66db0..a7170706b799bb436a7fd41250986f3aec8f8414 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt @@ -21,6 +21,7 @@ import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskE import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.subheader.OverviewSubHeaderItem import de.rki.coronawarnapp.presencetracing.checkins.CheckIn import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.checkins.common.locationName import de.rki.coronawarnapp.presencetracing.risk.TraceLocationCheckInRisk import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk @@ -147,13 +148,8 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( val riskEventItem = traceLocationCheckInRisksForDate .mapNotNull { - val locationVisit = visitsForDate.find { visit -> visit.checkInID == it.checkInId } - val checkIn = checkInList.find { checkIn -> checkIn.id == it.checkInId } - - return@mapNotNull when (locationVisit != null && checkIn != null) { - true -> RiskEventDataHolder(it, locationVisit, checkIn) - else -> null - } + val checkIn = checkInList.find { checkIn -> checkIn.id == it.checkInId } ?: return@mapNotNull null + RiskEventDataHolder(it, checkIn) }.toRiskEventItem() DayOverviewItem( @@ -209,7 +205,6 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( private data class RiskEventDataHolder( val traceLocationCheckInRisk: TraceLocationCheckInRisk, - val locationVisit: ContactDiaryLocationVisit, val checkIn: CheckIn ) @@ -234,7 +229,8 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( } val events = map { data -> - val name = data.locationVisit.contactDiaryLocation.locationName + val checkIn = data.checkIn + val name = checkIn.locationName val bulletPointColor: Int var riskInfoAddition: Int? @@ -252,7 +248,7 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( if (size < 2) riskInfoAddition = null - val description = data.checkIn.description + val description = checkIn.description RiskEventItem.Event( name = name, @@ -262,8 +258,6 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( ) } - if (events.isEmpty()) return null - return RiskEventItem( title = title, body = body, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt index 3e35ebfa07e11b1194ca6d8a214f785906ef1b10..740b2545e474b9cb6cd07ab37c74d066c2071add 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt @@ -1,8 +1,9 @@ package de.rki.coronawarnapp.coronatest -import androidx.annotation.VisibleForTesting import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.coronatest.errors.CoronaTestNotFoundException +import de.rki.coronawarnapp.coronatest.errors.DuplicateCoronaTestException +import de.rki.coronawarnapp.coronatest.errors.UnknownTestTypeException import de.rki.coronawarnapp.coronatest.migration.PCRTestMigration import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestGUID import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode @@ -71,44 +72,23 @@ class CoronaTestRepository @Inject constructor( private fun getProcessor(type: CoronaTest.Type) = processors.single { it.type == type } - suspend fun registerTest(registrationRequest: TestRegistrationRequest): CoronaTest = when (registrationRequest) { - is CoronaTestQRCode -> registerTestByQRCode(registrationRequest) - is CoronaTestTAN -> registerTestByTAN(registrationRequest) - else -> throw IllegalArgumentException("Unknown test request: $registrationRequest") - } + suspend fun registerTest(request: TestRegistrationRequest): CoronaTest { + Timber.tag(TAG).i("registerTest(request=%s)", request) - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal suspend fun registerTestByTAN(request: CoronaTestTAN): CoronaTest { - Timber.tag(TAG).i("registerTestByQRCode(request=%s)", request) // We check early, if there is no processor, crash early, "should" never happen though... val processor = getProcessor(request.type) val currentTests = internalData.updateBlocking { if (values.any { it.type == request.type }) { - throw IllegalStateException("There is already a test of this type: ${request.type}.") + throw DuplicateCoronaTestException("There is already a test of this type: ${request.type}.") } - val test = processor.create(request) - Timber.tag(TAG).i("Adding new test: %s", test) - - toMutableMap().apply { this[test.identifier] = test } - } - - return currentTests[request.identifier]!! - } - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal suspend fun registerTestByQRCode(request: CoronaTestQRCode): CoronaTest { - Timber.tag(TAG).i("registerTestByQRCode(request=%s)", request) - // We check early, if there is no processor, crash early, "should" never happen though... - val processor = getProcessor(request.type) - - val currentTests = internalData.updateBlocking { - if (values.any { it.type == request.type }) { - throw IllegalStateException("There is already a test of this type: ${request.type}.") + val test = when (request) { + is CoronaTestQRCode -> processor.create(request) + is CoronaTestTAN -> processor.create(request) + else -> throw UnknownTestTypeException("Unknown test request: $request") } - val test = processor.create(request) Timber.tag(TAG).i("Adding new test: %s", test) toMutableMap().apply { this[test.identifier] = test } @@ -188,7 +168,7 @@ class CoronaTestRepository @Inject constructor( } } - return refreshedData.values.toSet() + return refreshedData.values.filter { toRefresh.contains(it.identifier) }.toSet() } suspend fun clear() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/RATProfileSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/RATProfileSettings.kt index a9169309856e9b587dcf33eda9217066c637d026..c432743863efcaa82a5a02e6ee639e9c6babf88d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/RATProfileSettings.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/RATProfileSettings.kt @@ -1,10 +1,10 @@ package de.rki.coronawarnapp.coronatest.antigen.profile import android.content.Context +import androidx.core.content.edit import com.google.gson.Gson import dagger.Reusable import de.rki.coronawarnapp.util.di.AppContext -import de.rki.coronawarnapp.util.preferences.clearAndNotify import de.rki.coronawarnapp.util.preferences.createFlowPreference import de.rki.coronawarnapp.util.serialization.BaseGson import de.rki.coronawarnapp.util.serialization.fromJson @@ -35,9 +35,17 @@ class RATProfileSettings @Inject constructor( } ) - fun clear() = prefs.clearAndNotify() + val onboarded = prefs.createFlowPreference( + key = PREFS_KEY_ONBOARDED, + defaultValue = false + ) + + fun deleteProfile() = prefs.edit(commit = true) { + remove(PREFS_KEY_PROFILE) + } companion object { private const val PREFS_KEY_PROFILE = "ratprofile.settings.profile" + private const val PREFS_KEY_ONBOARDED = "ratprofile.settings.onboarded" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCard.kt new file mode 100644 index 0000000000000000000000000000000000000000..619587bd95a11a71a285a4095499c1a5d1af4bf5 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCard.kt @@ -0,0 +1,51 @@ +package de.rki.coronawarnapp.coronatest.antigen.profile + +import dagger.Reusable +import de.rki.coronawarnapp.util.TimeStamper +import org.joda.time.format.ISODateTimeFormat +import javax.inject.Inject + +@Reusable +class VCard @Inject constructor( + private val timeStamper: TimeStamper +) { + + /** + * Return V-Card format for [RATProfile] + * @return [String] + */ + fun create(ratProfile: RATProfile): String = ratProfile.run { + val lastName = lastName.escapeAll() + val firstName = firstName.escapeAll() + val fullName = buildString { + append(firstName) + if (lastName.isNotBlank()) { + append(" $lastName") + } + } + val city = city.escapeAll() + val street = street.escapeAll() + val zipCode = zipCode.escapeAll() + val phone = phone.escapeAll() + val email = email.escapeAll() + val birthDate = birthDate?.toString(ISODateTimeFormat.basicDate()).orEmpty() + val rev = timeStamper.nowUTC.toString(ISODateTimeFormat.basicDateTimeNoMillis()) // Time the vCard was updated + """ + BEGIN:VCARD + VERSION:4.0 + N:$lastName;$firstName;;; + FN:$fullName + BDAY:$birthDate + EMAIL;TYPE=home:$email + TEL;TYPE="cell,home":$phone + ADR;TYPE=home:;;$street;$city;;$zipCode + REV:$rev + END:VCARD + """.trimIndent() + } + + private fun String.escapeAll(): String = replace("\n", "") + .replace("\\", "\\\\") + .replace(",", "\\,") + .replace(";", "\\;") +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/DuplicateCoronaTestException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/DuplicateCoronaTestException.kt new file mode 100644 index 0000000000000000000000000000000000000000..13efa1802969c3785ee96c53bf823d09600b74b2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/DuplicateCoronaTestException.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.coronatest.errors + +class DuplicateCoronaTestException( + message: String +) : IllegalArgumentException(message) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/UnknownTestTypeException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/UnknownTestTypeException.kt new file mode 100644 index 0000000000000000000000000000000000000000..f68ab2721427b6ba3c284ded591ff17bbc1fa37c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/errors/UnknownTestTypeException.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.coronatest.errors + +class UnknownTestTypeException( + message: String +) : IllegalArgumentException(message) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt index 8c2a92737f1825e4b682066390375bdf4af833ee..7148ec4c4a807094fe8b8194172467ab383621b0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt @@ -34,7 +34,7 @@ class ShareTestResultNotification @Inject constructor( } fun showSharePositiveTestResultNotification(notificationId: Int, testType: CoronaTest.Type) { - Timber.d("showSharePositiveTestResultNotification(notificationId=$notificationId)") + Timber.tag(TAG).d("showSharePositiveTestResultNotification(notificationId=$notificationId)") val args = Bundle().apply { putSerializable("testType", testType) } @@ -59,6 +59,10 @@ class ShareTestResultNotification @Inject constructor( fun cancelSharePositiveTestResultNotification(testType: CoronaTest.Type) { notificationHelper.cancelFutureNotifications(POSITIVE_RESULT_NOTIFICATION_ID, testType) - Timber.v("Future positive test result notifications have been canceled") + Timber.tag(TAG).v("Future positive test result notifications have been canceled") + } + + companion object { + private val TAG = ShareTestResultNotification::class.simpleName } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt index 155b74a704e991dc9f1efaefde9105e0383e2660..fc4799ce95d70c5def84b4f89cc4c349e6c54ba6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.coronatest.notification +import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.latestPCRT import de.rki.coronawarnapp.coronatest.latestRAT @@ -27,48 +28,19 @@ class ShareTestResultNotificationService @Inject constructor( fun setup() { Timber.d("setup()") - - coronaTestRepository.coronaTests - .onEach { tests -> - - // schedule reminder if test wasn't submitted - tests.filter { test -> - test.isSubmissionAllowed && !test.isSubmitted - }.forEach { test -> - maybeScheduleSharePositiveTestResultReminder(test.type) - } - - // cancel the reminder when test is submitted - tests - .filter { it.isSubmitted } - .forEach { notification.cancelSharePositiveTestResultNotification(it.type) } - } - .catch { Timber.e(it, "Failed to schedule positive test result reminder.") } - .launchIn(appScope) - + schedulePositiveTestsReminder() // if no PCR test is stored or if it was deleted, we reset the reminder - coronaTestRepository.latestPCRT - .onEach { - if (it == null) { - resetSharePositiveTestResultNotification(PCR) - } - } - .catch { Timber.e(it, "Failed to reset positive test result reminder for PCR test.") } - .launchIn(appScope) - + resetPositivePCRTestReminder() // if no RAT test is stored or if it was deleted, we reset the reminder - coronaTestRepository.latestRAT - .onEach { - if (it == null) { - resetSharePositiveTestResultNotification(RAPID_ANTIGEN) - } - } - .catch { Timber.e(it, "Failed to reset positive test result reminder for RAT test.") } - .launchIn(appScope) + resetPositiveRATTestReminder() } fun maybeShowSharePositiveTestResultNotification(notificationId: Int, testType: CoronaTest.Type) { - Timber.d("maybeShowSharePositiveTestResultNotification(notificationId=$notificationId)") + Timber.tag(TAG).d( + "maybeShowSharePositiveTestResultNotification(notificationId=%s,testType=%s)", + notificationId, + testType + ) if (testType == PCR) { if (cwaSettings.numberOfRemainingSharePositiveTestResultRemindersPcr > 0) { cwaSettings.numberOfRemainingSharePositiveTestResultRemindersPcr -= 1 @@ -86,32 +58,79 @@ class ShareTestResultNotificationService @Inject constructor( } } + private fun resetPositivePCRTestReminder() { + Timber.tag(TAG).v("resetPositivePCRTestReminder") + coronaTestRepository.latestPCRT // This should not crash ,it is just a flow already constructed + .onEach { + if (it == null) { + resetSharePositiveTestResultNotification(PCR) + } + } + // Catch error in onEach block + .catch { it.reportProblem("Failed to reset positive test result reminder for PCR test.") } + .launchIn(appScope) + } + + private fun resetPositiveRATTestReminder() { + Timber.tag(TAG).v("resetPositiveRATTestReminder") + coronaTestRepository.latestRAT // This should not crash ,it is just a flow already constructed + .onEach { + if (it == null) { + resetSharePositiveTestResultNotification(RAPID_ANTIGEN) + } + } + // Catch error in onEach block + .catch { it.reportProblem("Failed to reset positive test result reminder for RAT test.") } + .launchIn(appScope) + } + + private fun schedulePositiveTestsReminder() { + Timber.tag(TAG).v("schedulePositiveTestsReminder") + coronaTestRepository.coronaTests // This should not crash ,it is just a flow already constructed + .onEach { tests -> + // schedule reminder if test wasn't submitted + tests.filter { test -> + test.isSubmissionAllowed && !test.isSubmitted + }.forEach { test -> + maybeScheduleSharePositiveTestResultReminder(test.type) + } + + // cancel the reminder when test is submitted + tests.filter { it.isSubmitted } + .forEach { notification.cancelSharePositiveTestResultNotification(it.type) } + } + // Catch error in onEach block + .catch { it.reportProblem("Failed to schedule positive test result reminder.") } + .launchIn(appScope) + } + private fun maybeScheduleSharePositiveTestResultReminder(testType: CoronaTest.Type) { when (testType) { PCR -> { if (cwaSettings.numberOfRemainingSharePositiveTestResultRemindersPcr < 0) { - Timber.v("Schedule positive test result notification for PCR test") + Timber.tag(TAG).v("Schedule positive test result notification for PCR test") cwaSettings.numberOfRemainingSharePositiveTestResultRemindersPcr = POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT notification.scheduleSharePositiveTestResultReminder(testType) } else { - Timber.v("Positive test result notification for PCR test has already been scheduled") + Timber.tag(TAG).v("Positive test result notification for PCR test has already been scheduled") } } RAPID_ANTIGEN -> { if (cwaSettings.numberOfRemainingSharePositiveTestResultRemindersRat < 0) { - Timber.v("Schedule positive test result notification for RAT test") + Timber.tag(TAG).v("Schedule positive test result notification for RAT test") cwaSettings.numberOfRemainingSharePositiveTestResultRemindersRat = POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT notification.scheduleSharePositiveTestResultReminder(testType) } else { - Timber.v("Positive test result notification for RAT test has already been scheduled") + Timber.tag(TAG).v("Positive test result notification for RAT test has already been scheduled") } } } } private fun resetSharePositiveTestResultNotification(testType: CoronaTest.Type) { + Timber.tag(TAG).v("resetSharePositiveTestResultNotification(testType=%s)", testType) notification.cancelSharePositiveTestResultNotification(testType) when (testType) { @@ -119,6 +138,10 @@ class ShareTestResultNotificationService @Inject constructor( RAPID_ANTIGEN -> cwaSettings.numberOfRemainingSharePositiveTestResultRemindersRat = Int.MIN_VALUE } - Timber.v("Share positive test result notification counter has been reset for all test types") + Timber.tag(TAG).v("Share positive test result notification counter has been reset for all test types") + } + + companion object { + private val TAG = ShareTestResultNotificationService::class.simpleName } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt index 53d57593af693c5fa6cde7db77d4266f26edda1a..49633e3decd06846753a897897b235a16409cbdd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/PcrQrCodeExtractor.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.coronatest.qrcode +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor import java.util.regex.Pattern import javax.inject.Inject @@ -8,9 +9,9 @@ class PcrQrCodeExtractor @Inject constructor() : QrCodeExtractor<CoronaTestQRCod override fun canHandle(rawString: String): Boolean = rawString.startsWith(prefix, ignoreCase = true) override fun extract(rawString: String): CoronaTestQRCode.PCR { - return CoronaTestQRCode.PCR( - extractGUID(rawString) - ) + val guid = extractGUID(rawString) + PcrQrCodeCensor.lastGUID = guid + return CoronaTestQRCode.PCR(guid) } private fun extractGUID(rawString: String): CoronaTestGUID { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt index c1a95544ce0b759c88f7dc2f792d35c17cbcd194..c5881f97364a97bd6c5f8c795b3b1df7c1c72df0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.coronatest.qrcode import com.google.common.io.BaseEncoding import com.google.gson.Gson import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor import de.rki.coronawarnapp.util.HashExtensions.toSHA256 import de.rki.coronawarnapp.util.hashing.isSha256Hash import de.rki.coronawarnapp.util.serialization.fromJson @@ -22,6 +23,14 @@ class RapidAntigenQrCodeExtractor @Inject constructor() : QrCodeExtractor<Corona Timber.v("extract(rawString=%s)", rawString) val payload = CleanPayload(extractData(rawString)) + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = rawString, + hash = payload.hash, + firstName = payload.firstName, + lastName = payload.lastName, + dateOfBirth = payload.dateOfBirth + ) + payload.requireValidData() return CoronaTestQRCode.RapidAntigen( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/server/VerificationServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/server/VerificationServer.kt index 3ae32ead140aebe45a3ebc590eec9eaee0950f0f..78d80f3f1620b50164a26ca76d3f09e81609ec0a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/server/VerificationServer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/server/VerificationServer.kt @@ -6,6 +6,7 @@ import de.rki.coronawarnapp.util.PaddingTool.requestPadding import de.rki.coronawarnapp.util.security.HashHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.joda.time.Duration import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -114,6 +115,12 @@ class VerificationServer @Inject constructor( const val PADDING_LENGTH_BODY_TAN_FAKE = 31 + VERIFICATION_BODY_FILL const val DUMMY_REGISTRATION_TOKEN = "11111111-2222-4444-8888-161616161616" + /** + * Test is available for this long on the server. + * After this period the server will delete it and return PENDING if the regtoken is polled again. + */ + val TEST_AVAILABLBILITY = Duration.standardDays(60) + private const val TAG = "VerificationServer" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..a666b118caa8da2abe4494e11e98e74ea1f9ad8f --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensions.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.coronatest.type + +import org.joda.time.Duration +import org.joda.time.Instant + +fun CoronaTest.isOlderThan21Days(nowUTC: Instant): Boolean { + return Duration(registeredAt, nowUTC).standardDays > 21 +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt index 9c01a64e92c3555b3bb0e79c6bae69fe73345203..d866dca57e230c320f0322319a5b0c7f4a2e9676 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt @@ -14,17 +14,19 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_NEGATIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_PENDING import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED +import de.rki.coronawarnapp.coronatest.server.VerificationServer import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.CoronaTestService +import de.rki.coronawarnapp.coronatest.type.isOlderThan21Days import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector import de.rki.coronawarnapp.datadonation.analytics.modules.registeredtest.TestResultDataCollector import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.http.BadRequestException import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.util.TimeStamper -import de.rki.coronawarnapp.worker.BackgroundConstants import org.joda.time.Duration import org.joda.time.Instant import timber.log.Timber @@ -103,15 +105,33 @@ class PCRProcessor @Inject constructor( test as PCRCoronaTest if (test.isSubmitted) { - Timber.tag(TAG).w("Not refreshing, we have already submitted.") + Timber.tag(TAG).w("Not polling, we have already submitted.") return test } - val newTestResult = submissionService.asyncRequestTestResult(test.registrationToken).let { - Timber.tag(TAG).d("Raw test result was %s", it) - testResultDataCollector.updatePendingTestResultReceivedTime(it) + val nowUTC = timeStamper.nowUTC + val isOlderThan21Days = test.isOlderThan21Days(nowUTC) - it.toValidatedResult() + if (isOlderThan21Days && test.testResult == PCR_REDEEMED) { + Timber.tag(TAG).w("Not polling, test is older than 21 days.") + return test + } + + val newTestResult = try { + submissionService.asyncRequestTestResult(test.registrationToken).let { + Timber.tag(TAG).d("Raw test result was %s", it) + testResultDataCollector.updatePendingTestResultReceivedTime(it) + + it.toValidatedResult() + } + } catch (e: BadRequestException) { + if (isOlderThan21Days) { + Timber.tag(TAG).w("HTTP 400 error after 21 days, remapping to PCR_REDEEMED.") + PCR_REDEEMED + } else { + Timber.tag(TAG).v("Unexpected HTTP 400 error, rethrowing...") + throw e + } } if (newTestResult == PCR_POSITIVE) { @@ -119,9 +139,9 @@ class PCRProcessor @Inject constructor( } test.copy( - testResult = check60PlusDays(test, newTestResult), + testResult = check60Days(test, newTestResult), testResultReceivedAt = determineReceivedDate(test, newTestResult), - lastUpdatedAt = timeStamper.nowUTC, + lastUpdatedAt = nowUTC, lastError = null ) } catch (e: Exception) { @@ -133,13 +153,13 @@ class PCRProcessor @Inject constructor( } } - // After 60 days, the previously EXPIRED test is deleted from the server, and it will return pending again. - private fun check60PlusDays(test: CoronaTest, newResult: CoronaTestResult): CoronaTestResult { - val calculateDays = Duration(test.registeredAt, timeStamper.nowUTC).standardDays - Timber.tag(TAG).d("Calculated test age: %d days", calculateDays) + // After 60 days, the previously EXPIRED test is deleted from the server, and it may return pending again. + private fun check60Days(test: CoronaTest, newResult: CoronaTestResult): CoronaTestResult { + val calculateDays = Duration(test.registeredAt, timeStamper.nowUTC) + Timber.tag(TAG).d("Calculated test age: %d days, newResult=%s", calculateDays.standardDays, newResult) - return if (newResult == PCR_OR_RAT_PENDING && calculateDays >= BackgroundConstants.POLLING_VALIDITY_MAX_DAYS) { - Timber.tag(TAG).d("$calculateDays is exceeding the maximum polling duration") + return if (newResult == PCR_OR_RAT_PENDING && calculateDays > VerificationServer.TEST_AVAILABLBILITY) { + Timber.tag(TAG).d("$calculateDays is exceeding the test availability.") PCR_REDEEMED } else { newResult diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/execution/PCRResultRetrievalWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/execution/PCRResultRetrievalWorker.kt index 74c5e98d67fe5a1bbaf3e5fc138846e167dbc4f6..0a9076d068d9f72f8bc9c1c6a41ba569d0cffeb8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/execution/PCRResultRetrievalWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/execution/PCRResultRetrievalWorker.kt @@ -9,7 +9,6 @@ import dagger.assisted.AssistedInject import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.latestPCRT import de.rki.coronawarnapp.coronatest.type.CoronaTest -import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import de.rki.coronawarnapp.worker.BackgroundConstants import kotlinx.coroutines.flow.first @@ -42,12 +41,9 @@ class PCRResultRetrievalWorker @AssistedInject constructor( return Result.success() } - Timber.tag(TAG).d(" $id Running task.") - val coronaTest = coronaTestRepository.refresh( - type = CoronaTest.Type.PCR - ).single() as PCRCoronaTest - val testResult = coronaTest.testResult - Timber.tag(TAG).d("$id: Test result retrieved is $testResult") + Timber.tag(TAG).v("$id Running PCR test result refresh.") + coronaTestRepository.refresh(type = CoronaTest.Type.PCR) + Timber.tag(TAG).d("$id: PCR test result refreshed.") return Result.success() } catch (e: Exception) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/notification/PCRTestResultAvailableNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/notification/PCRTestResultAvailableNotificationService.kt index 1a2dd6872c13a09509dd74816e9bae3b6f8dbb1a..ac6b2653a2132dfa85687d8d2d1f6ff9363f079a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/notification/PCRTestResultAvailableNotificationService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/notification/PCRTestResultAvailableNotificationService.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.coronatest.type.pcr.notification import android.content.Context import androidx.navigation.NavDeepLinkBuilder import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.errors.CoronaTestNotFoundException import de.rki.coronawarnapp.coronatest.latestPCRT import de.rki.coronawarnapp.coronatest.server.CoronaTestResult import de.rki.coronawarnapp.coronatest.type.common.TestResultAvailableNotificationService @@ -64,7 +65,11 @@ class PCRTestResultAvailableNotificationService @Inject constructor( notSentYet && isInteresting -> { Timber.tag(TAG).d("Showing PCR test result notification.") showTestResultAvailableNotification(test) - coronaTestRepository.updateResultNotification(identifier = test.identifier, sent = true) + try { + coronaTestRepository.updateResultNotification(identifier = test.identifier, sent = true) + } catch (e: CoronaTestNotFoundException) { + Timber.tag(TAG).e(e, "updateResultNotification failed") + } notificationHelper.cancelCurrentNotification( NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt index 2a9d412af608b844ebea4b85bbc209f10e14171d..13654af995730337cf71d2b4e257b9dfda554354 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt @@ -13,15 +13,17 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_NEGATIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_PENDING import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED +import de.rki.coronawarnapp.coronatest.server.VerificationServer import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.CoronaTestService +import de.rki.coronawarnapp.coronatest.type.isOlderThan21Days import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.http.BadRequestException import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.util.TimeStamper -import de.rki.coronawarnapp.worker.BackgroundConstants import org.joda.time.Duration import org.joda.time.Instant import timber.log.Timber @@ -82,19 +84,37 @@ class RapidAntigenProcessor @Inject constructor( test as RACoronaTest if (test.isSubmitted) { - Timber.tag(TAG).w("Not refreshing, we have already submitted.") + Timber.tag(TAG).w("Not polling, we have already submitted.") return test } - val newTestResult = submissionService.asyncRequestTestResult(test.registrationToken).let { - Timber.tag(TAG).v("Raw test result was %s", it) - it.toValidatedResult() + val nowUTC = timeStamper.nowUTC + val isOlderThan21Days = test.isOlderThan21Days(nowUTC) + + if (isOlderThan21Days && test.testResult == RAT_REDEEMED) { + Timber.tag(TAG).w("Not polling, test is older than 21 days.") + return test + } + + val newTestResult = try { + submissionService.asyncRequestTestResult(test.registrationToken).let { + Timber.tag(TAG).v("Raw test result was %s", it) + it.toValidatedResult() + } + } catch (e: BadRequestException) { + if (isOlderThan21Days) { + Timber.tag(TAG).w("HTTP 400 error after 21 days, remapping to RAT_REDEEMED.") + RAT_REDEEMED + } else { + Timber.tag(TAG).v("Unexpected HTTP 400 error, rethrowing...") + throw e + } } test.copy( - testResult = check60PlusDays(test, newTestResult), + testResult = check60Days(test, newTestResult), testResultReceivedAt = determineReceivedDate(test, newTestResult), - lastUpdatedAt = timeStamper.nowUTC, + lastUpdatedAt = nowUTC, lastError = null ) } catch (e: Exception) { @@ -106,16 +126,15 @@ class RapidAntigenProcessor @Inject constructor( } } - // After 60 days, the previously EXPIRED test is deleted from the server, and it will return pending again. - private fun check60PlusDays(test: CoronaTest, newResult: CoronaTestResult): CoronaTestResult { - val calculateDays = Duration(test.registeredAt, timeStamper.nowUTC).standardDays - Timber.tag(TAG).d("Calculated test age: %d days", calculateDays) + // After 60 days, the previously EXPIRED test is deleted from the server, and it may return pending again. + private fun check60Days(test: CoronaTest, newResult: CoronaTestResult): CoronaTestResult { + val calculateDays = Duration(test.registeredAt, timeStamper.nowUTC) + Timber.tag(TAG).d("Calculated test age: %d days, newResult=%s", calculateDays.standardDays, newResult) - return if ( - (newResult == PCR_OR_RAT_PENDING || newResult == RAT_PENDING) && - calculateDays >= BackgroundConstants.POLLING_VALIDITY_MAX_DAYS + return if ((newResult == PCR_OR_RAT_PENDING || newResult == RAT_PENDING) && + calculateDays > VerificationServer.TEST_AVAILABLBILITY ) { - Timber.tag(TAG).d("$calculateDays is exceeding the maximum polling duration") + Timber.tag(TAG).d("$calculateDays is exceeding the test availability.") RAT_REDEEMED } else { newResult diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/execution/RAResultRetrievalWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/execution/RAResultRetrievalWorker.kt index f390af2d9402a3847e19e32251bb96a3764a1d7e..33e3b2f49757e30b24a3cbceaba88c3acc272035 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/execution/RAResultRetrievalWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/execution/RAResultRetrievalWorker.kt @@ -46,8 +46,9 @@ class RAResultRetrievalWorker @AssistedInject constructor( Timber.tag(TAG).w("There is no RapidAntigen test available!?") return Result.success() } - + Timber.tag(TAG).v("$id Running RA test result refresh.") coronaTestRepository.refresh(CoronaTest.Type.RAPID_ANTIGEN) + Timber.tag(TAG).d("$id: RA test result refreshed.") val nowUTC = timeStamper.nowUTC val days = Duration(rat.registeredAt, nowUTC).standardDays diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/notification/RATTestResultAvailableNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/notification/RATTestResultAvailableNotificationService.kt index 49fd8b04f991b4596609052462ea70fa4e9bb9aa..25a66a488359f4e7478f9a7d4671eef062d52693 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/notification/RATTestResultAvailableNotificationService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/notification/RATTestResultAvailableNotificationService.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.coronatest.type.rapidantigen.notification import android.content.Context import androidx.navigation.NavDeepLinkBuilder import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.errors.CoronaTestNotFoundException import de.rki.coronawarnapp.coronatest.latestRAT import de.rki.coronawarnapp.coronatest.server.CoronaTestResult import de.rki.coronawarnapp.coronatest.type.common.TestResultAvailableNotificationService @@ -63,7 +64,11 @@ class RATTestResultAvailableNotificationService @Inject constructor( notSentYet && isInteresting -> { Timber.tag(TAG).d("Showing RA test result notification.") showTestResultAvailableNotification(test) - coronaTestRepository.updateResultNotification(identifier = test.identifier, sent = true) + try { + coronaTestRepository.updateResultNotification(identifier = test.identifier, sent = true) + } catch (e: CoronaTestNotFoundException) { + Timber.tag(TAG).e(e, "updateResultNotification failed") + } notificationHelper.cancelCurrentNotification( NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt index 9c415cdc5e307527452650674c60ee45f184a5e8..ba1448d41337ec126c6f9de5dc27f234dba858b3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt @@ -35,14 +35,14 @@ class DownloadDiagnosisKeysTask @Inject constructor( private val timeStamper: TimeStamper, private val settings: DownloadDiagnosisKeysSettings, private val coronaTestRepository: CoronaTestRepository, -) : Task<DownloadDiagnosisKeysTask.Progress, Task.Result> { +) : Task<DownloadDiagnosisKeysTask.Progress, DownloadDiagnosisKeysTask.Result> { private val internalProgress = ConflatedBroadcastChannel<Progress>() override val progress: Flow<Progress> = internalProgress.asFlow() private var isCanceled = false - override suspend fun run(arguments: Task.Arguments): Task.Result { + override suspend fun run(arguments: Task.Arguments): Result { val rollbackItems = mutableListOf<RollbackItem>() try { Timber.d("Running with arguments=%s", arguments) @@ -55,7 +55,7 @@ class DownloadDiagnosisKeysTask @Inject constructor( */ if (!enfClient.isTracingEnabled.first()) { Timber.tag(TAG).w("EN is not enabled, skipping RetrieveDiagnosisKeys") - return object : Task.Result {} + return Result() } throwIfCancelled() @@ -76,14 +76,14 @@ class DownloadDiagnosisKeysTask @Inject constructor( if (!exposureConfig.isDeviceTimeCorrect) { Timber.tag(TAG).w("Aborting, Device time is incorrect, offset=%s", exposureConfig.localOffset) - return object : Task.Result {} + return Result() } val now = timeStamper.nowUTC if (exposureConfig.maxExposureDetectionsPerUTCDay == 0) { Timber.tag(TAG).w("Exposure detections are disabled! maxExposureDetectionsPerUTCDay=0") - return object : Task.Result {} + return Result() } val trackedExposureDetections = enfClient.latestTrackedExposureDetection().first() @@ -93,12 +93,12 @@ class DownloadDiagnosisKeysTask @Inject constructor( if (!isUpdateToEnfV2 && wasLastDetectionPerformedRecently(now, exposureConfig, trackedExposureDetections)) { // At most one detection every 6h Timber.tag(TAG).i("task aborted, because detection was performed recently") - return object : Task.Result {} + return Result() } if (!isUpdateToEnfV2 && hasRecentDetectionAndNoNewFiles(now, keySyncResult, trackedExposureDetections)) { Timber.tag(TAG).i("task aborted, last check was within 24h, and there are no new files") - return object : Task.Result {} + return Result() } val availableKeyFiles = keySyncResult.availableKeys.map { it.path } @@ -114,10 +114,10 @@ class DownloadDiagnosisKeysTask @Inject constructor( // remember version code of this execution for next time settings.updateLastVersionCodeToCurrent() - val isAllowedToSubmitKeys = coronaTestRepository.coronaTests.first().any { it.isSubmissionAllowed } - if (isAllowedToSubmitKeys) { - Timber.tag(TAG).i("task aborted, positive test result") - return object : Task.Result {} + val isPositive = coronaTestRepository.coronaTests.first().any { it.isPositive } + if (isPositive) { + Timber.tag(TAG).i("EW risk calculation aborted, positive test result available.") + return Result() } Timber.tag(TAG).d("Attempting submission to ENF") @@ -129,7 +129,7 @@ class DownloadDiagnosisKeysTask @Inject constructor( internalProgress.send(Progress.ApiSubmissionFinished) - return object : Task.Result {} + return Result() } catch (error: Exception) { Timber.tag(TAG).e(error) @@ -215,6 +215,8 @@ class DownloadDiagnosisKeysTask @Inject constructor( isCanceled = true } + class Result : Task.Result + sealed class Progress : Task.Progress { object ApiSubmissionStarted : Progress() object ApiSubmissionFinished : Progress() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncTool.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncTool.kt index 3269607dd6f1104423965cf157ad08bf48f7f85a..caa3143bc4f03a53ba486f072c18d00f0fa2e42a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncTool.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncTool.kt @@ -9,6 +9,8 @@ import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKey import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKeyInfo.Type import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository +import de.rki.coronawarnapp.exception.http.CwaServerError +import de.rki.coronawarnapp.exception.http.CwaUnknownHostException import de.rki.coronawarnapp.exception.http.NetworkConnectTimeoutException import de.rki.coronawarnapp.storage.DeviceStorage import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc @@ -54,8 +56,8 @@ class HourPackageSyncTool @Inject constructor( val missingHours = targetLocations.mapNotNull { try { determineMissingHours(it, forceIndexLookup || keysWereRevoked) - } catch (e: NetworkConnectTimeoutException) { - Timber.tag(TAG).i("missing hours sync failed due to network timeout") + } catch (e: CwaServerError) { + Timber.tag(TAG).i("missing hours sync failed due to network problems") return SyncResult(successful = false, newPackages = emptyList()) } } @@ -151,6 +153,9 @@ class HourPackageSyncTool @Inject constructor( } catch (e: NetworkConnectTimeoutException) { Timber.tag(TAG).e(e, "Failed to get today's hour due - not going to delete the cache.") throw e + } catch (e: CwaUnknownHostException) { + Timber.tag(TAG).e(e, "Failed to get today's hour index - unknown host.") + throw e } catch (e: IOException) { Timber.tag(TAG).e(e, "failed to get today's hour index.") emptyList() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tracing/DefaultTracingStatus.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tracing/DefaultTracingStatus.kt index 025a3ff9f4fe24325066579a53e31b750ad18179..dfba1e6444250b859870b1f3dc9d7d4371d5a716 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tracing/DefaultTracingStatus.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/tracing/DefaultTracingStatus.kt @@ -122,9 +122,7 @@ class DefaultTracingStatus @Inject constructor( ) private suspend fun isEnabled(): Boolean = try { - client.isEnabled.await().also { - Timber.tag(TAG).v("Tracing isEnabled=$it") - } + client.isEnabled.await() } catch (e: Throwable) { Timber.tag(TAG).w(e, "Failed to determine tracing status.") throw e diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/GeneralNotifications.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/GeneralNotifications.kt index 9bffd29f9e43a7f8d9de165bb8793bee5067296a..7361b2843c64625c7ff6493d07a2b65b2c844849 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/GeneralNotifications.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/GeneralNotifications.kt @@ -44,7 +44,7 @@ class GeneralNotifications @Inject constructor( @TargetApi(Build.VERSION_CODES.O) private fun setupNotificationChannel() { - Timber.d("setupChannel()") + Timber.tag(TAG).d("setupChannel()") val channel = NotificationChannel( MAIN_CHANNEL_ID, @@ -70,12 +70,12 @@ class GeneralNotifications @Inject constructor( val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager manager.cancel(pendingIntent) - Timber.v("Canceled future notifications with id: %s", notificationId) + Timber.tag(TAG).v("Canceled future notifications with id: %s", notificationId) } fun cancelCurrentNotification(notificationId: Int) { NotificationManagerCompat.from(context).cancel(notificationId) - Timber.v("Canceled notifications with id: %s", notificationId) + Timber.tag(TAG).v("Canceled notifications with id: %s", notificationId) } fun scheduleRepeatingNotification( @@ -138,12 +138,13 @@ class GeneralNotifications @Inject constructor( isNotificationChannelSetup = true setupNotificationChannel() } - Timber.i("Showing notification for ID=$notificationId: %s", notification) + Timber.tag(TAG).i("Showing notification for ID=$notificationId: %s", notification) notificationManagerCompat.notify(notificationId, notification) } companion object { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal const val MAIN_CHANNEL_ID = "de.rki.coronawarnapp.notification.exposureNotificationChannelId" + private const val TAG = "GeneralNotifications" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreator.kt index e770f6db91734c0c3e171d327ca43563f082681b..deef3a5c16cfbdb77844280e949dbd97e3be972c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreator.kt @@ -8,13 +8,12 @@ import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.presencetracing.checkins.common.locationName import de.rki.coronawarnapp.presencetracing.checkins.split.splitByMidnightUTC import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc -import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone import kotlinx.coroutines.flow.first import org.joda.time.Duration import org.joda.time.Seconds -import org.joda.time.format.DateTimeFormat import timber.log.Timber import javax.inject.Inject import kotlin.math.roundToLong @@ -47,7 +46,7 @@ class ContactJournalCheckInEntryCreator @Inject constructor( private suspend fun CheckIn.createLocationEntry(): ContactDiaryLocation { Timber.d("Creating new contact diary location from %s", this) val location = DefaultContactDiaryLocation( - locationName = toLocationName(), + locationName = locationName, traceLocationID = traceLocationId ) @@ -56,21 +55,6 @@ class ContactJournalCheckInEntryCreator @Inject constructor( .also { Timber.d("Created %s and added it to contact journal db", it) } } - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - fun CheckIn.toLocationName(): String { - val nameParts = mutableListOf(description, address) - - if (traceLocationStart != null && traceLocationEnd != null) { - if (traceLocationStart.millis > 0 && traceLocationEnd.millis > 0) { - val formattedStartDate = traceLocationStart.toUserTimeZone().toString(DateTimeFormat.shortDateTime()) - val formattedEndDate = traceLocationEnd.toUserTimeZone().toString(DateTimeFormat.shortDateTime()) - nameParts.add("$formattedStartDate - $formattedEndDate") - } - } - - return nameParts.joinToString(separator = ", ") - } - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun CheckIn.toLocationVisit(location: ContactDiaryLocation): ContactDiaryLocationVisit { // Duration column is set by calculating the time difference in minutes between Check-in StartDate diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtension.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtension.kt new file mode 100644 index 0000000000000000000000000000000000000000..4d39591c739eab7dbed59a92d19b78f87ef1eb60 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtension.kt @@ -0,0 +1,20 @@ +package de.rki.coronawarnapp.presencetracing.checkins.common + +import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone +import org.joda.time.format.DateTimeFormat + +val CheckIn.locationName: String + get() { + val nameParts = mutableListOf(description, address) + + if (traceLocationStart != null && traceLocationEnd != null) { + if (traceLocationStart.millis > 0 && traceLocationEnd.millis > 0) { + val formattedStartDate = traceLocationStart.toUserTimeZone().toString(DateTimeFormat.shortDateTime()) + val formattedEndDate = traceLocationEnd.toUserTimeZone().toString(DateTimeFormat.shortDateTime()) + nameParts.add("$formattedStartDate - $formattedEndDate") + } + } + + return nameParts.joinToString(separator = ", ") + } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/qrcode/QrCodeGenerator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/qrcode/QrCodeGenerator.kt index 38d00f815ad46162e5adc6150bbf0e3c1d3d7dfb..d4645b71f6decac284bc3720cb37070a12ac63b6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/qrcode/QrCodeGenerator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/checkins/qrcode/QrCodeGenerator.kt @@ -38,7 +38,10 @@ class QrCodeGenerator @Inject constructor( val hints = mapOf( EncodeHintType.ERROR_CORRECTION to correctionLevel, - EncodeHintType.MARGIN to margin + EncodeHintType.MARGIN to margin, + // we cannot use Charsets.UTF_8 as zxing calls toString internally and some android version + // return the class name and not the charset name + EncodeHintType.CHARACTER_SET to Charsets.UTF_8.name() ) return MultiFormatWriter().encode( input, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt index b5c6b195dc3b54b186f8f5d71edd1c8a7a39fb64..ea7b2eece6db44ee9736e3c964240429d3775a14 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt @@ -2,9 +2,11 @@ package de.rki.coronawarnapp.presencetracing.risk.execution import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.bugreporting.reportProblem +import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut import de.rki.coronawarnapp.presencetracing.risk.calculation.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.calculation.PresenceTracingRiskMapper import de.rki.coronawarnapp.presencetracing.risk.storage.PresenceTracingRiskRepository @@ -17,9 +19,9 @@ import de.rki.coronawarnapp.util.TimeStamper import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull import org.joda.time.Duration -import org.joda.time.Instant import timber.log.Timber import javax.inject.Inject import javax.inject.Provider @@ -31,7 +33,9 @@ class PresenceTracingWarningTask @Inject constructor( private val presenceTracingRiskRepository: PresenceTracingRiskRepository, private val traceWarningRepository: TraceWarningRepository, private val checkInsRepository: CheckInRepository, - private val presenceTracingRiskMapper: PresenceTracingRiskMapper + private val presenceTracingRiskMapper: PresenceTracingRiskMapper, + private val coronaTestRepository: CoronaTestRepository, + private val autoCheckOut: AutoCheckOut, ) : Task<PresenceTracingWarningTaskProgress, PresenceTracingWarningTask.Result> { private val internalProgress = ConflatedBroadcastChannel<PresenceTracingWarningTaskProgress>() @@ -42,6 +46,12 @@ class PresenceTracingWarningTask @Inject constructor( override suspend fun run(arguments: Task.Arguments): Result = try { Timber.d("Running with arguments=%s", arguments) + autoCheckOut.apply { + Timber.tag(TAG).d("Processing overdue check-outs before risk calculation.") + processOverDueCheckouts() + refreshAlarm() + } + try { doWork() } catch (e: Exception) { @@ -59,7 +69,6 @@ class PresenceTracingWarningTask @Inject constructor( } private suspend fun doWork(): Result { - val nowUTC = timeStamper.nowUTC checkCancel() Timber.tag(TAG).d("Resetting config to make sure latest changes are considered.") @@ -75,7 +84,7 @@ class PresenceTracingWarningTask @Inject constructor( } else { Timber.tag(TAG).w("WarningPackage sync failed: %s", syncResult) presenceTracingRiskRepository.reportCalculation(successful = false) - return Result(calculatedAt = nowUTC) + return Result() } presenceTracingRiskRepository.deleteStaleData() @@ -89,7 +98,13 @@ class PresenceTracingWarningTask @Inject constructor( presenceTracingRiskRepository.reportCalculation(successful = true) - return Result(calculatedAt = nowUTC) + return Result() + } + + val isPositive = coronaTestRepository.coronaTests.first().any { it.isPositive } + if (isPositive) { + Timber.tag(TAG).i("PT risk calculation aborted, positive test result available.") + return Result() } val unprocessedPackages = traceWarningRepository.unprocessedWarningPackages.firstOrNull() ?: emptyList() @@ -100,7 +115,7 @@ class PresenceTracingWarningTask @Inject constructor( presenceTracingRiskRepository.reportCalculation(successful = true) - return Result(calculatedAt = nowUTC) + return Result() } Timber.tag(TAG).d("Running check-in matcher.") @@ -131,7 +146,7 @@ class PresenceTracingWarningTask @Inject constructor( matcherResult.processedPackages.map { it.warningPackage.packageId } ) - return Result(calculatedAt = nowUTC) + return Result() } private fun checkCancel() { @@ -143,9 +158,7 @@ class PresenceTracingWarningTask @Inject constructor( isCanceled = true } - data class Result( - val calculatedAt: Instant - ) : Task.Result + class Result : Task.Result data class Config( override val executionTimeout: Duration = Duration.standardMinutes(9), diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt index d81f7eff26394e0c622943efb89dde6e84aab444..cab719c6c8faeeb36b4c8c43ed266f39f42c166f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt @@ -1,6 +1,5 @@ package de.rki.coronawarnapp.risk -import android.content.Context import com.google.android.gms.nearby.exposurenotification.ExposureWindow import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData @@ -13,7 +12,6 @@ import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.ENFClient import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection -import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut import de.rki.coronawarnapp.risk.EwRiskLevelResult.FailureReason import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult import de.rki.coronawarnapp.risk.storage.RiskLevelStorage @@ -21,10 +19,8 @@ import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskCancellationException import de.rki.coronawarnapp.task.TaskFactory import de.rki.coronawarnapp.task.common.DefaultProgress -import de.rki.coronawarnapp.util.ConnectivityHelper.isNetworkEnabled import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.device.BackgroundModeStatus -import de.rki.coronawarnapp.util.di.AppContext import kotlinx.coroutines.channels.ConflatedBroadcastChannel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow @@ -38,7 +34,6 @@ import javax.inject.Provider @Suppress("ReturnCount", "LongParameterList") class RiskLevelTask @Inject constructor( private val riskLevels: RiskLevels, - @AppContext private val context: Context, private val enfClient: ENFClient, private val timeStamper: TimeStamper, private val backgroundModeStatus: BackgroundModeStatus, @@ -48,7 +43,6 @@ class RiskLevelTask @Inject constructor( private val keyCacheRepository: KeyCacheRepository, private val coronaTestRepository: CoronaTestRepository, private val analyticsExposureWindowCollector: AnalyticsExposureWindowCollector, - private val autoCheckOut: AutoCheckOut, ) : Task<DefaultProgress, EwRiskLevelTaskResult> { private val internalProgress = ConflatedBroadcastChannel<DefaultProgress>() @@ -59,12 +53,6 @@ class RiskLevelTask @Inject constructor( override suspend fun run(arguments: Task.Arguments): EwRiskLevelTaskResult = try { Timber.d("Running with arguments=%s", arguments) - autoCheckOut.apply { - Timber.tag(TAG).d("Processing overdue check-outs before risk calculation.") - processOverDueCheckouts() - refreshAlarm() - } - val configData: ConfigData = appConfigProvider.getAppConfig() determineRiskLevelResult(configData).also { @@ -109,14 +97,6 @@ class RiskLevelTask @Inject constructor( ) } - if (!isNetworkEnabled(context)) { - Timber.i("Risk not calculated, internet unavailable.") - return EwRiskLevelTaskResult( - calculatedAt = nowUTC, - failureReason = FailureReason.NO_INTERNET - ) - } - if (!enfClient.isTracingEnabled.first()) { Timber.i("Risk not calculated, tracing is disabled.") return EwRiskLevelTaskResult( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt index 70549aee1c17eb4acd80e6bdc2b7004a6e08b1ad..02092bb927dd58f9f18a50efabd426d89e152d1b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/SubmissionRepository.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -44,9 +45,9 @@ class SubmissionRepository @Inject constructor( val currentSymptoms = submissionSettings.symptoms // to be used by new submission flow screens - fun giveConsentToSubmission(type: CoronaTest.Type) { + suspend fun giveConsentToSubmission(type: CoronaTest.Type) { Timber.tag(TAG).v("giveConsentToSubmission(type=%s)", type) - scope.launch { + withContext(scope.coroutineContext) { val test = coronaTestRepository.coronaTests.first().singleOrNull { it.type == type } ?: throw IllegalStateException("No test of type $type available") Timber.tag(TAG).v("giveConsentToSubmission(type=$type): %s", test) @@ -55,9 +56,9 @@ class SubmissionRepository @Inject constructor( } // to be used by new submission flow screens - fun revokeConsentToSubmission(type: CoronaTest.Type) { + suspend fun revokeConsentToSubmission(type: CoronaTest.Type) { Timber.tag(TAG).v("revokeConsentToSubmission(type=%s)", type) - scope.launch { + withContext(scope.coroutineContext) { val test = coronaTestRepository.coronaTests.first().singleOrNull { it.type == type } ?: throw IllegalStateException("No test of type $type available") @@ -66,9 +67,9 @@ class SubmissionRepository @Inject constructor( } // to be set to true once the user has opened and viewed their test result - fun setViewedTestResult(type: CoronaTest.Type) { + suspend fun setViewedTestResult(type: CoronaTest.Type) { Timber.tag(TAG).v("setViewedTestResult(type=%s)", type) - scope.launch { + withContext(scope.coroutineContext) { val test = coronaTestRepository.coronaTests.first().singleOrNull { it.type == type } ?: throw IllegalStateException("No test of type $type available") @@ -76,10 +77,10 @@ class SubmissionRepository @Inject constructor( } } - fun refreshTest(type: CoronaTest.Type? = null) { + suspend fun refreshTest(type: CoronaTest.Type? = null) { Timber.tag(TAG).v("refreshTest(type=%s)", type) - scope.launch { + withContext(scope.coroutineContext) { coronaTestRepository.refresh(type = type) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/behavior/BehaviorInfoRow.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/behavior/BehaviorInfoRow.kt index 9c7a0c565df370eaf870b874126789dc6d5ce63d..e544d84568fc66297b3f56d636080a43c9b4c536 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/behavior/BehaviorInfoRow.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/behavior/BehaviorInfoRow.kt @@ -2,6 +2,8 @@ package de.rki.coronawarnapp.tracing.ui.details.items.behavior import android.content.Context import android.content.res.ColorStateList +import android.graphics.PorterDuff +import android.os.Build import android.util.AttributeSet import android.view.LayoutInflater import android.view.View @@ -14,6 +16,8 @@ import androidx.core.content.withStyledAttributes import androidx.core.view.ViewCompat import androidx.core.widget.ImageViewCompat import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.BuildVersionWrap +import de.rki.coronawarnapp.util.hasAPILevel import de.rki.coronawarnapp.util.setUrl class BehaviorInfoRow @JvmOverloads constructor( @@ -61,7 +65,12 @@ class BehaviorInfoRow @JvmOverloads constructor( } fun setBackgroundTint(@ColorInt color: Int) { - ViewCompat.setBackgroundTintList(iconBackground, ColorStateList.valueOf(color)) + when { + BuildVersionWrap.hasAPILevel(Build.VERSION_CODES.LOLLIPOP_MR1) -> + ViewCompat.setBackgroundTintList(iconBackground, ColorStateList.valueOf(color)) + else -> + iconBackground.background.setColorFilter(color, PorterDuff.Mode.SRC_OVER) + } } fun setForegroundTint(@ColorInt color: Int) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragment.kt index bc08e9d60a4b95f3d7a5ae90c1c3e1a7dcaca571..3c2da3aa538b007d7881ed8f4e521e02460f50af 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragment.kt @@ -46,14 +46,13 @@ class SettingsTracingFragment : Fragment(R.layout.fragment_settings_tracing), Au vm.tracingSettingsState.observe2(this) { state -> binding.settingsTracingState = state - binding.settingsTracingSwitchRow.settingsSwitchRow.apply { + binding.switchRow.apply { when (state) { TracingSettingsState.BluetoothDisabled, TracingSettingsState.LocationDisabled -> setOnClickListener(null) TracingSettingsState.TracingInactive, TracingSettingsState.TracingActive -> setOnClickListener { - val switch = binding.settingsTracingSwitchRow.settingsSwitchRowSwitch - onTracingToggled(!switch.isChecked) + onTracingToggled(!binding.switchRow.isChecked) } } } @@ -73,7 +72,7 @@ class SettingsTracingFragment : Fragment(R.layout.fragment_settings_tracing), Au } vm.isTracingSwitchChecked.observe2(this) { checked -> - binding.settingsTracingSwitchRow.settingsSwitchRowSwitch.isChecked = checked + binding.switchRow.setChecked(checked) } vm.ensErrorEvents.observe2(this) { error -> @@ -98,12 +97,6 @@ class SettingsTracingFragment : Fragment(R.layout.fragment_settings_tracing), Au popBackStack() } - val switch = binding.settingsTracingSwitchRow.settingsSwitchRowSwitch - switch.setOnCheckedChangeListener { view, isChecked -> - if (!view.isPressed) return@setOnCheckedChangeListener - onTracingToggled(isChecked) - } - val bluetooth = binding.settingsTracingStatusBluetooth.tracingStatusCardButton bluetooth.setOnClickListener { ExternalActionHelper.toMainSettings(requireContext()) @@ -121,7 +114,10 @@ class SettingsTracingFragment : Fragment(R.layout.fragment_settings_tracing), Au } private fun onTracingToggled(isChecked: Boolean) { - vm.onTracingToggled(isChecked) + if (isChecked) + vm.turnTracingOn() + else + vm.turnTracingOff() } private fun navigateToInteroperability() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt index d8eedf1c334362173e74a94fbc781f09acff7a8b..3ec86a0a71c21b36e1b2abb6f63d27f99f56b2ef 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt @@ -104,19 +104,9 @@ class SettingsTracingFragmentViewModel @AssistedInject constructor( } ) - fun onTracingToggled(isChecked: Boolean) { + fun turnTracingOn() { try { - if (isChecked) { - tracingPermissionHelper.startTracing() - } else { - isTracingSwitchChecked.postValue(false) - launch { - if (InternalExposureNotificationClient.asyncIsEnabled()) { - InternalExposureNotificationClient.asyncStop() - exposureWindowRiskWorkScheduler.setPeriodicRiskCalculation(enabled = false) - } - } - } + tracingPermissionHelper.startTracing() } catch (exception: Exception) { exception.report( ExceptionCategory.EXPOSURENOTIFICATION, @@ -126,6 +116,24 @@ class SettingsTracingFragmentViewModel @AssistedInject constructor( } } + fun turnTracingOff() { + isTracingSwitchChecked.postValue(false) + launch { + try { + if (InternalExposureNotificationClient.asyncIsEnabled()) { + InternalExposureNotificationClient.asyncStop() + exposureWindowRiskWorkScheduler.setPeriodicRiskCalculation(enabled = false) + } + } catch (exception: Exception) { + exception.report( + ExceptionCategory.EXPOSURENOTIFICATION, + SettingsTracingFragment.TAG, + null + ) + } + } + } + fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { tracingPermissionHelper.handleActivityResult(requestCode, resultCode, data) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/calendar/CalendarWeekDayView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/calendar/CalendarWeekDayView.kt index 52ff504e3d219e5fee43e5c51e86c5dea38a4afe..2b352a1f664aef5e82ac794950b62671a701e8b9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/calendar/CalendarWeekDayView.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/calendar/CalendarWeekDayView.kt @@ -5,6 +5,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout import android.widget.TextView +import androidx.core.widget.TextViewCompat import de.rki.coronawarnapp.R /** @@ -43,9 +44,9 @@ class CalendarWeekDayView @JvmOverloads constructor( textView.text = text if (isSelected) { - textView.setTextAppearance(R.style.calendarWeekDaySelected) + TextViewCompat.setTextAppearance(textView, R.style.calendarWeekDaySelected) } else { - textView.setTextAppearance(R.style.calendarWeekDayNormal) + TextViewCompat.setTextAppearance(textView, R.style.calendarWeekDayNormal) } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/RATProfileUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/RATProfileUIModule.kt index 21a76225622d3fe5375e956e10c0bd7507114bb4..61208100d9be7ddcd09c80be0639ca7773fda25b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/RATProfileUIModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/RATProfileUIModule.kt @@ -4,10 +4,20 @@ import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.ui.coronatest.rat.profile.create.RATProfileCreateFragment import de.rki.coronawarnapp.ui.coronatest.rat.profile.create.RATProfileCreateFragmentModule +import de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding.RATProfileOnboardingFragment +import de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding.RATProfileOnboardingFragmentModule +import de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragment +import de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragmentModule @Module internal abstract class RATProfileUIModule { @ContributesAndroidInjector(modules = [RATProfileCreateFragmentModule::class]) abstract fun ratProfileCreateFragment(): RATProfileCreateFragment + + @ContributesAndroidInjector(modules = [RATProfileQrCodeFragmentModule::class]) + abstract fun ratProfileQrCodeFragment(): RATProfileQrCodeFragment + + @ContributesAndroidInjector(modules = [RATProfileOnboardingFragmentModule::class]) + abstract fun ratProfileOnboardingFragment(): RATProfileOnboardingFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragment.kt index 8d32df661e7f5e6e1d971edf6c3c842de2e9b356..2ef3dcaca4e697d9a5cdcfa6f27bbc309cea85eb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragment.kt @@ -40,6 +40,11 @@ class RATProfileCreateFragment : Fragment(R.layout.rat_profile_create_fragment), // Birth date birthDateInputEdit.setOnClickListener { openDatePicker() } + birthDateInputEdit.doAfterTextChanged { + if (it.toString().isBlank()) { + viewModel.birthDateChanged(null) + } + } // Address streetInputEdit.doAfterTextChanged { viewModel.streetChanged(it.toString()) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragmentViewModel.kt index 36ca71e288a1a5d80f7ae3fc88c57e7161ca3473..27861b540f066645e23703f9d8a43c8c6c3ca957 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/create/RATProfileCreateFragmentViewModel.kt @@ -41,7 +41,7 @@ class RATProfileCreateFragmentViewModel @AssistedInject constructor( } } - fun birthDateChanged(birthDate: LocalDate) { + fun birthDateChanged(birthDate: LocalDate?) { profileData.apply { value = value?.copy(birthDate = birthDate) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragment.kt index 18e46be246132dba85aa54dd3c114c5661320446..bf3e0cb317ce6a714a2680a1b90f5282b3c18821 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragment.kt @@ -5,23 +5,38 @@ import android.view.View import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.RatProfileOnboardingFragmentBinding +import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject -class RATProfileOnboardingFragment : Fragment(R.layout.rat_profile_onboarding_fragment) { +class RATProfileOnboardingFragment : Fragment(R.layout.rat_profile_onboarding_fragment), AutoInject { private val binding: RatProfileOnboardingFragmentBinding by viewBindingLazy() + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + + private val viewModel: RATProfileOnboardingFragmentViewModel by cwaViewModels { viewModelFactory } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(binding) { toolbar.setNavigationOnClickListener { popBackStack() } - nextButton.setOnClickListener { + viewModel.onNext() doNavigate( RATProfileOnboardingFragmentDirections .actionRatProfileOnboardingFragmentToRatProfileCreateFragment() ) } + + ratProfileOnboardingPrivacy.setOnClickListener { + doNavigate( + RATProfileOnboardingFragmentDirections + .actionRatProfileOnboardingFragmentToPrivacyFragment() + ) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..1b2bdad689f7a2064187143ab0a38b478dd51dbc --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentModule.kt @@ -0,0 +1,18 @@ +package de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class RATProfileOnboardingFragmentModule { + @Binds + @IntoMap + @CWAViewModelKey(RATProfileOnboardingFragmentViewModel::class) + abstract fun ratProfileOnboardingFragment( + factory: RATProfileOnboardingFragmentViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..0160a51dc00649b821b570ef84af638e5063de50 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/onboarding/RATProfileOnboardingFragmentViewModel.kt @@ -0,0 +1,19 @@ +package de.rki.coronawarnapp.ui.coronatest.rat.profile.onboarding + +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory + +class RATProfileOnboardingFragmentViewModel @AssistedInject constructor( + private val ratProfileSettings: RATProfileSettings, +) : CWAViewModel() { + + fun onNext() { + ratProfileSettings.onboarded.update { true } + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<RATProfileOnboardingFragmentViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/ProfileQrCodeNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/ProfileQrCodeNavigation.kt new file mode 100644 index 0000000000000000000000000000000000000000..030aa4d4a7b6e8d06c5aca636d7f9b12c224250d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/ProfileQrCodeNavigation.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode + +sealed class ProfileQrCodeNavigation { + object Back : ProfileQrCodeNavigation() + object SubmissionConsent : ProfileQrCodeNavigation() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragment.kt index 4bc5b2120fed99eae14aca2a4a0458e45357461a..cedefa8d72b7ed4319d2c6d1bcda846703dc9931 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragment.kt @@ -2,18 +2,106 @@ package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode import android.os.Bundle import android.view.View +import android.widget.LinearLayout +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.text.bold +import androidx.core.text.buildSpannedString import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.dialog.MaterialAlertDialogBuilder import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile import de.rki.coronawarnapp.databinding.RatProfileQrCodeFragmentBinding +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.joinToSpannable import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject +import kotlin.math.abs -class RATProfileQrCodeFragment : Fragment(R.layout.rat_profile_qr_code_fragment) { +class RATProfileQrCodeFragment : Fragment(R.layout.rat_profile_qr_code_fragment), AutoInject { + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + + private val viewModel: RATProfileQrCodeFragmentViewModel by cwaViewModels { viewModelFactory } private val binding: RatProfileQrCodeFragmentBinding by viewBindingLazy() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setToolbarOverlay() + binding.apply { + appBarLayout.addOnOffsetChangedListener( + AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> + val alpha = 1.0f - abs(verticalOffset / (appBarLayout.totalScrollRange.toFloat() * 0.5f)) + title.alpha = alpha + } + ) + + nextButton.setOnClickListener { viewModel.onNext() } + toolbar.setNavigationOnClickListener { viewModel.onClose() } + toolbar.setOnMenuItemClickListener { + confirmDeletionDialog() + true + } + } + viewModel.profile.observe(viewLifecycleOwner) { personProfile -> + with(binding) { + progressBar.hide() + personProfile.profile?.let { bindPersonInfo(it) } + qrCodeImage.setImageBitmap(personProfile.bitmap) + } + } + + viewModel.events.observe(viewLifecycleOwner) { + when (it) { + ProfileQrCodeNavigation.Back -> popBackStack() + ProfileQrCodeNavigation.SubmissionConsent -> + findNavController().navigate(R.id.submissionConsentFragment) + } + } + } + + private fun confirmDeletionDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.rat_qr_code_profile_dialog_title)) + .setMessage(getString(R.string.rat_qr_code_profile_dialog_message)) + .setPositiveButton(getString(R.string.rat_qr_code_profile_dialog_positive_button)) { _, _ -> + viewModel.deleteProfile() + } + .setNegativeButton(getString(R.string.rat_qr_code_profile_dialog_negative_button)) { _, _ -> + // No-Op + } + .show() + } + + private fun bindPersonInfo(ratProfile: RATProfile) = with(ratProfile) { + val name = buildSpannedString { bold { append("$firstName $lastName") } } + val birthDate = birthDate?.let { + getString( + R.string.rat_qr_code_profile_birth_date, + birthDate.toString("dd.MM.yyyy").orEmpty() + ) + }.orEmpty() + + val address = "$zipCode $city" + binding.profileInfo.text = arrayOf(name, birthDate, street, address, phone, email) + .filter { it.isNotBlank() } + .joinToSpannable("\n") + } + + private fun setToolbarOverlay() { + val width = requireContext().resources.displayMetrics.widthPixels + + val params: CoordinatorLayout.LayoutParams = binding.nestedScrollView.layoutParams + as (CoordinatorLayout.LayoutParams) + + val textParams = binding.title.layoutParams as (LinearLayout.LayoutParams) + textParams.bottomMargin = (width / 2) - 24 /* 24 is space between screen border and QrCode */ + binding.title.requestLayout() /* 24 is space between screen border and QrCode */ - binding.closeButton.setOnClickListener { popBackStack() } + val behavior: AppBarLayout.ScrollingViewBehavior = params.behavior as (AppBarLayout.ScrollingViewBehavior) + behavior.overlayTop = (width / 2) - 24 } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..b35a9dbd0d30474b9cc7f5a70f7ed867e4eb4c3c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentModule.kt @@ -0,0 +1,18 @@ +package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class RATProfileQrCodeFragmentModule { + @Binds + @IntoMap + @CWAViewModelKey(RATProfileQrCodeFragmentViewModel::class) + abstract fun ratProfileQrCodeFragmentViewModel( + factory: RATProfileQrCodeFragmentViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..fad1095b4c4fffbf13b19fec30be729de5101e5d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/coronatest/rat/profile/qrcode/RATProfileQrCodeFragmentViewModel.kt @@ -0,0 +1,72 @@ +package de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode + +import android.graphics.Bitmap +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.coronatest.antigen.profile.VCard +import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.map +import timber.log.Timber + +class RATProfileQrCodeFragmentViewModel @AssistedInject constructor( + private val ratProfileSettings: RATProfileSettings, + private val qrCodeGenerator: QrCodeGenerator, + private val vCard: VCard, + dispatcherProvider: DispatcherProvider, +) : CWAViewModel() { + + val profile: LiveData<PersonProfile> = ratProfileSettings.profile.flow + .map { profile -> + PersonProfile( + profile, + profile.qrCode() + ) + }.asLiveData(context = dispatcherProvider.Default) + + val events = SingleLiveEvent<ProfileQrCodeNavigation>() + + fun deleteProfile() { + Timber.d("deleteProfile") + ratProfileSettings.deleteProfile() + events.postValue(ProfileQrCodeNavigation.Back) + } + + fun onClose() { + Timber.d("onClose") + events.postValue(ProfileQrCodeNavigation.Back) + } + + fun onNext() { + Timber.d("onNext") + events.postValue(ProfileQrCodeNavigation.SubmissionConsent) + } + + private suspend fun RATProfile?.qrCode(): Bitmap? = + try { + if (this != null) { + qrCodeGenerator.createQrCode(vCard.create(this)) + } else { + Timber.d("No Profile available") + null + } + } catch (e: Exception) { + Timber.e(e, "Failed to generate profile Qr Code") + null + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<RATProfileQrCodeFragmentViewModel> +} + +data class PersonProfile( + val profile: RATProfile?, + val bitmap: Bitmap? +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt index 0921d2f093d0da987c76535badef71cc4db242f2..bda8566ff07baf6857daa64eafd76c1da555013b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt @@ -136,6 +136,10 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { }?.show() } } + + viewModel.errorEvent.observe2(this) { + it.toErrorDialogBuilder(requireContext()).show() + } } override fun onResume() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt index 922423eaaf0b071377d7af7e80cbc67caa0e69ad..2ab3ce80e760ff68cba51e7909ff93506910d316 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt @@ -8,6 +8,7 @@ import dagger.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.CoronaTestConfig import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.errors.CoronaTestNotFoundException import de.rki.coronawarnapp.coronatest.latestPCRT import de.rki.coronawarnapp.coronatest.latestRAT import de.rki.coronawarnapp.coronatest.testErrorsSingleEvent @@ -74,6 +75,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import timber.log.Timber @Suppress("LongParameterList") class HomeFragmentViewModel @AssistedInject constructor( @@ -100,6 +102,7 @@ class HomeFragmentViewModel @AssistedInject constructor( val openFAQUrlEvent = SingleLiveEvent<Unit>() val openIncompatibleEvent = SingleLiveEvent<Unit>() val openTraceLocationOrganizerFlow = SingleLiveEvent<Unit>() + val errorEvent = SingleLiveEvent<Throwable>() val tracingHeaderState: LiveData<TracingHeaderState> = tracingStatus.generalStatus .map { it.toHeaderState() } @@ -286,7 +289,6 @@ class HomeFragmentViewModel @AssistedInject constructor( ) { tracingItem, testPCR, testRAT, statsData, coronaTestParameters -> val statePCR = testPCR.toSubmissionState() val stateRAT = testRAT.toSubmissionState(timeStamper.nowUTC, coronaTestParameters) - val bothTestStates = setOf(statePCR, stateRAT) mutableListOf<HomeItem>().apply { when { statePCR is SubmissionStatePCR.TestPositive || statePCR is SubmissionStatePCR.SubmissionDone -> { @@ -374,7 +376,12 @@ class HomeFragmentViewModel @AssistedInject constructor( fun refreshRequiredData() { launch { - submissionRepository.refreshTest() + try { + submissionRepository.refreshTest() + } catch (e: CoronaTestNotFoundException) { + Timber.e(e, "refreshTest failed") + errorEvent.postValue(e) + } tracingRepository.refreshRiskLevel() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeMenu.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeMenu.kt index 306b9598a3a37440a3d8c1bd48fc5ac1db362b12..4189f6bd9082e22bf122324b1e4f19159be4b696 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeMenu.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeMenu.kt @@ -1,7 +1,7 @@ package de.rki.coronawarnapp.ui.main.home import android.view.MenuItem.SHOW_AS_ACTION_ALWAYS -import android.widget.Toolbar +import androidx.appcompat.widget.Toolbar import androidx.navigation.NavController import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt index 29976a36cb93c1afb1b495e72df06335aca4aab1..a753ca9424cfd7cc3e55a74b383adc8f5776627c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt @@ -8,6 +8,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor import de.rki.coronawarnapp.contactdiary.util.CWADateTimeFormatPatternFactory.shortDatePattern import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.presencetracing.locations.TraceLocationCreator @@ -68,6 +69,8 @@ class TraceLocationCreateViewModel @AssistedInject constructor( defaultCheckInLengthInMinutes = checkInLength.standardMinutes.toInt() ) + TraceLocationCensor.dataToCensor = userInput + launch { try { val traceLocation = traceLocationCreator.createTraceLocation(userInput) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsFragment.kt index 9ceb7d072ae22fd404df73d9b8ae58f55ffcfb2c..1613ba258ef82499d699259d0592dab2ea57f958 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsFragment.kt @@ -3,7 +3,7 @@ package de.rki.coronawarnapp.ui.settings.analytics import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent -import androidx.core.view.isVisible +import androidx.core.view.isGone import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSettingsPrivacyPreservingAnalyticsBinding @@ -29,30 +29,14 @@ class SettingsPrivacyPreservingAnalyticsFragment : super.onViewCreated(view, savedInstanceState) binding.apply { - - // Privacy Preserving Analytics Switch - val updateAnalyticsSwitch = - settingsPpaSwitchRow.settingsSwitchRowSwitch - // Additional click target to toggle switch - val updateAnalyticsRow = - settingsPpaSwitchRow.settingsSwitchRow - - settingsPpaHeader.headerButtonBack.buttonIcon.setOnClickListener { + settingsPpaHeader.setNavigationOnClickListener { popBackStack() } - updateAnalyticsSwitch.setOnCheckedChangeListener { view, _ -> - // Make sure that listener is called by user interaction - if (!view.isPressed) return@setOnCheckedChangeListener - + settingsPpaSwitchRow.setUserToggleListener { _, _ -> viewModel.analyticsToggleEnabled() } - // Additional click target to toggle switch - updateAnalyticsRow.setOnClickListener { - if (updateAnalyticsRow.isEnabled) viewModel.analyticsToggleEnabled() - } - federalStateRow.setOnClickListener { doNavigate( SettingsPrivacyPreservingAnalyticsFragmentDirections @@ -88,15 +72,22 @@ class SettingsPrivacyPreservingAnalyticsFragment : } viewModel.settingsPrivacyPreservingAnalyticsState.observe2(this) { - binding.ageGroupRowBody.text = it.getAgeGroupRowBodyText(requireContext()) + binding.ageGroupRow.apply { + isGone = !it.isAgeGroupVisible + setSubtitle(it.getAgeGroupRowBodyText(requireContext())) + } - binding.districtRow.isVisible = it.isDistrictRowVisible() - binding.districtRowBody.text = it.getDistrictRowBodyText(requireContext()) + binding.districtRow.apply { + isGone = !it.isDistrictRowVisible + setSubtitle(it.getDistrictRowBodyText(requireContext())) + } - binding.federalStateRowBody.text = it.getFederalStateRowBodyText(requireContext()) + binding.federalStateRow.apply { + isGone = !it.isFederalStateRowVisible + setSubtitle(it.getFederalStateRowBodyText(requireContext())) + } - binding.settingsPpaSwitchRow.status = it.isSettingsPpaSwitchOn() - binding.settingsPpaSwitchRow.statusText = it.getSettingsPpaSwitchRowStateText(requireContext()) + binding.settingsPpaSwitchRow.setChecked(it.isAnalyticsEnabled, notify = false) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsState.kt index 341c13378e1bc8b493999ba76b18558d6543c254..0bda141e6891c75b0e9cc54c29caa7d9e2261fcc 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsState.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/analytics/SettingsPrivacyPreservingAnalyticsState.kt @@ -12,24 +12,21 @@ data class SettingsPrivacyPreservingAnalyticsState( val federalState: PpaData.PPAFederalState, val district: Districts.District? ) { + val isAgeGroupVisible: Boolean + get() = isAnalyticsEnabled + fun getAgeGroupRowBodyText(context: Context) = context.getString(ageGroup.labelStringRes) - fun isDistrictRowVisible() = - federalState != PpaData.PPAFederalState.FEDERAL_STATE_UNSPECIFIED + val isDistrictRowVisible + get() = federalState != PpaData.PPAFederalState.FEDERAL_STATE_UNSPECIFIED && isAnalyticsEnabled fun getDistrictRowBodyText(context: Context) = district?.districtName ?: context.getString(R.string.analytics_userinput_district_unspecified) + val isFederalStateRowVisible: Boolean + get() = isAnalyticsEnabled + fun getFederalStateRowBodyText(context: Context) = context.getString(federalState.labelStringRes) - - fun isSettingsPpaSwitchOn() = - isAnalyticsEnabled - - fun getSettingsPpaSwitchRowStateText(context: Context) = - when (isAnalyticsEnabled) { - true -> context.getString(R.string.settings_on) - false -> context.getString(R.string.settings_off) - } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragment.kt index 38572c8ceb7966ed2fc29bd74e82239049356488..bd9eac9b26b758bca23ed18cc867bd19a15fc84b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragment.kt @@ -6,10 +6,10 @@ import android.view.accessibility.AccessibilityEvent import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSettingsNotificationsBinding -import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.util.ExternalActionHelper import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels @@ -43,49 +43,16 @@ class NotificationSettingsFragment : } private fun setButtonOnClickListener() { - // Notifications about risk status - val updateRiskNotificationSwitch = - binding.settingsSwitchRowNotificationsRisk.settingsSwitchRowSwitch - // Additional click target to toggle switch - val updateRiskNotificationRow = - binding.settingsSwitchRowNotificationsRisk.settingsSwitchRow - // Notifications about test status - val updateTestNotificationSwitch = - binding.settingsSwitchRowNotificationsTest.settingsSwitchRowSwitch - // Additional click target to toggle switch - val updateTestNotificationRow = - binding.settingsSwitchRowNotificationsTest.settingsSwitchRow - // Settings - val settingsRow = binding.settingsNotificationsCard.tracingStatusCardButton - val goBack = - binding.settingsNotificationsHeader.headerButtonBack.buttonIcon - // Update Risk - updateRiskNotificationSwitch.setOnCheckedChangeListener { view, _ -> - // Make sure that listener is called by user interaction - if (!view.isPressed) return@setOnCheckedChangeListener - + binding.settingsSwitchRowNotificationsRisk.setOnClickListener { vm.toggleNotificationsRiskEnabled() } - // Additional click target to toggle switch - updateRiskNotificationRow.setOnClickListener { - if (updateRiskNotificationRow.isEnabled) vm.toggleNotificationsRiskEnabled() - } - // Update Test - updateTestNotificationSwitch.setOnCheckedChangeListener { view, _ -> - // Make sure that listener is called by user interaction - if (!view.isPressed) return@setOnCheckedChangeListener - + binding.settingsSwitchRowNotificationsTest.setOnClickListener { vm.toggleNotificationsTestEnabled() } - // Additional click target to toggle switch - updateTestNotificationRow.setOnClickListener { - if (updateTestNotificationRow.isEnabled) vm.toggleNotificationsTestEnabled() - } - goBack.setOnClickListener { - (activity as MainActivity).goBack() + binding.settingsNotificationsHeader.setNavigationOnClickListener { + popBackStack() } - // System Settings - settingsRow.setOnClickListener { + binding.settingsNotificationsCard.tracingStatusCardButton.setOnClickListener { ExternalActionHelper.toNotifications(requireContext()) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsBackgroundState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsBackgroundState.kt index 470cf323458ff6951634072f284800d2dbd6a843..4217b4972ae671d19b2f115cd251ee68d39631d0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsBackgroundState.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsBackgroundState.kt @@ -2,10 +2,13 @@ package de.rki.coronawarnapp.ui.settings.start import android.content.Context import android.graphics.drawable.Drawable +import android.os.Build import androidx.annotation.ColorInt import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.BuildVersionWrap import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat import de.rki.coronawarnapp.util.ContextExtensions.getDrawableCompat +import de.rki.coronawarnapp.util.hasAPILevel data class SettingsBackgroundState( val isEnabled: Boolean @@ -34,4 +37,9 @@ data class SettingsBackgroundState( fun getBackgroundPriorityText(c: Context): String = c.getString( if (isEnabled) R.string.settings_on else R.string.settings_off ) + + /** + * Whether the "background priority" row should be visible in the settings + */ + fun showBackgroundPrioritySettings(): Boolean = BuildVersionWrap.hasAPILevel(Build.VERSION_CODES.M) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt index 7d0738a27ebf2986f7ce76644367b4ea627c501e..9a46d82aebba0fa406f8dba7e51723aa88e09d19 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt @@ -89,9 +89,6 @@ class SubmissionDeletionWarningViewModel @AssistedInject constructor( } catch (err: Exception) { mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) err.report(ExceptionCategory.INTERNAL) - } finally { - // TODO Should not be necessary? What new data would we - submissionRepository.refreshTest(type = CoronaTest.Type.PCR) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt index 3b16c6f61832089e3e981cf1d006eee4ad428890..11f7585630af3e8e2d67608088233528a34872d7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt @@ -48,11 +48,17 @@ class SubmissionDispatcherFragment : Fragment(R.layout.fragment_submission_dispa SubmissionDispatcherFragmentDirections .actionSubmissionDispatcherFragmentToSubmissionConsentFragment() ) - is SubmissionNavigationEvents.NavigateToCreateProfile -> + is SubmissionNavigationEvents.NavigateToCreateProfile -> { + val ratGraph = findNavController().graph.findNode(R.id.rapid_test_profile_nav_graph) as NavGraph + ratGraph.startDestination = if (it.onboarded) + R.id.ratProfileCreateFragment + else R.id.ratProfileOnboardingFragment + doNavigate( SubmissionDispatcherFragmentDirections .actionSubmissionDispatcherFragmentToRapidTestProfileNavGraph() ) + } is SubmissionNavigationEvents.NavigateToOpenProfile -> { val ratGraph = findNavController().graph.findNode(R.id.rapid_test_profile_nav_graph) as NavGraph diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt index 69b7c6e662c9b7a071f9c2735f2ab6809f753455..f6876b62aeebd0f5662569e1a26073a0fd7e8e66 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.asLiveData import com.google.android.gms.common.api.ApiException import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.nearby.modules.tekhistory.TEKHistoryProvider @@ -79,8 +78,6 @@ class SubmissionConsentViewModel @AssistedInject constructor( private suspend fun validateAndRegister(qrCodeString: String) { try { val coronaTestQRCode = qrCodeValidator.validate(qrCodeString) - // TODO this needs to be adapted to work for different types - QRCodeCensor.lastGUID = coronaTestQRCode.registrationIdentifier qrCodeValidationState.postValue(QrCodeRegistrationStateProcessor.ValidationState.SUCCESS) val coronaTest = submissionRepository.testForType(coronaTestQRCode.type).first() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt index b49dad70291cd84b7433f48fac0d66a8bcc99720..4222a676fd91f1b08fe2e5dd4987fd02629c090f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt @@ -3,7 +3,6 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.coronatest.type.CoronaTest @@ -43,8 +42,6 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor( suspend fun startQrCodeRegistration(rawResult: String, isConsentGiven: Boolean) { try { val coronaTestQRCode = qrCodeValidator.validate(rawResult) - // TODO this needs to be adapted to work for different types - QRCodeCensor.lastGUID = coronaTestQRCode.registrationIdentifier qrCodeValidationState.postValue(QrCodeRegistrationStateProcessor.ValidationState.SUCCESS) val coronaTest = submissionRepository.testForType(coronaTestQRCode.type).first() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt index dadb6672622aee60d7d16d7732f0da963f30bba4..5e30dd35e67d393d7c1eb6d979cf24dc95330955 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt @@ -98,10 +98,6 @@ class SubmissionTestResultAvailableViewModel @AssistedInject constructor( } ) - init { - submissionRepository.refreshTest(type = testType) - } - fun goBack() { showCloseDialog.postValue(Unit) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt index b55a94539cb59288b33790cfe83d423f3fdec9f5..de7b107fc1a7de7c54e045733ad2374e48083bf6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt @@ -89,9 +89,6 @@ class SubmissionTanViewModel @AssistedInject constructor( } catch (err: Exception) { registrationState.postValue(ApiRequestState.FAILED) err.report(ExceptionCategory.INTERNAL) - } finally { - // TODO Should not be necessary? What new data would we - submissionRepository.refreshTest(type = CoronaTest.Type.PCR) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt index 70ceed8db075032a7a58a2900e5b42e549bfdfa7..f71c5e1797b38095585d8b26ebbbbbe004b31faf 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt @@ -14,6 +14,7 @@ import androidx.annotation.DimenRes import androidx.core.view.children import androidx.core.widget.doOnTextChanged import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat import de.rki.coronawarnapp.util.getDrawableCompat import java.util.Locale import kotlin.math.max @@ -107,9 +108,9 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs tanDigit.setTextColor( if (Tan.isTanCharacterValid(text)) - resources.getColor(R.color.colorTextPrimary1, null) + context.getColorCompat(R.color.colorTextPrimary1) else - resources.getColor(R.color.colorTextSemanticRed, null) + context.getColorCompat(R.color.colorTextSemanticRed) ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidFragment.kt index 260b40ed42ac2e2ac116bac144dba491046fd0c4..0214619816cb56365f8a727bb575cf764c31198d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidFragment.kt @@ -37,6 +37,8 @@ class SubmissionTestResultInvalidFragment : Fragment(R.layout.fragment_submissio override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.onTestOpened() + binding.apply { submissionTestResultButtonInvalidRemoveTest.setOnClickListener { removeTestAfterConfirmation() } submissionTestResultHeader.headerButtonBack.buttonIcon.setOnClickListener { popBackStack() } @@ -54,7 +56,6 @@ class SubmissionTestResultInvalidFragment : Fragment(R.layout.fragment_submissio override fun onResume() { super.onResume() binding.submissionTestResultContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) - viewModel.onTestOpened() } private fun removeTestAfterConfirmation() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeFragment.kt index 082c4a3b8bd6a1da185ffabdde0718ee0ef2e5e5..7e164e0881f6b3549839d1a379d1c2d295b72110 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeFragment.kt @@ -37,6 +37,8 @@ class SubmissionTestResultNegativeFragment : Fragment(R.layout.fragment_submissi override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.onTestOpened() + binding.apply { submissionTestResultButtonNegativeRemoveTest.setOnClickListener { removeTestAfterConfirmation() } submissionTestResultHeader.headerButtonBack.buttonIcon.setOnClickListener { popBackStack() } @@ -54,7 +56,6 @@ class SubmissionTestResultNegativeFragment : Fragment(R.layout.fragment_submissi override fun onResume() { super.onResume() binding.submissionTestResultContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) - viewModel.onTestOpened() } private fun removeTestAfterConfirmation() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt index 096b1c81a56c7455a3f398ea0f8683c87a1d676a..31e7e029ebd06b913f46ce5e2c62a49d3e44ae7c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt @@ -7,6 +7,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder import de.rki.coronawarnapp.databinding.FragmentSubmissionTestResultPendingBinding import de.rki.coronawarnapp.exception.http.CwaClientError import de.rki.coronawarnapp.exception.http.CwaServerError @@ -63,7 +64,7 @@ class SubmissionTestResultPendingFragment : Fragment(R.layout.fragment_submissio binding.apply { submissionTestResultButtonPendingRefresh.setOnClickListener { - pendingViewModel.refreshDeviceUIState() + pendingViewModel.updateTestResult() binding.submissionTestResultSection.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } @@ -90,12 +91,14 @@ class SubmissionTestResultPendingFragment : Fragment(R.layout.fragment_submissio pendingViewModel.routeToScreen.observe2(this) { it?.let { doNavigate(it) } ?: navigateToMainScreen() } + pendingViewModel.errorEvent.observe2(this) { + it.toErrorDialogBuilder(requireContext()).show() + } } override fun onResume() { super.onResume() binding.submissionTestResultContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) - pendingViewModel.refreshDeviceUIState() skipInitialTestResultRefresh = false pendingViewModel.cwaWebExceptionLiveData.observeOnce(this.viewLifecycleOwner) { exception -> handleError(exception) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt index d7300a8b70e8f2db57eb47152aaed28e85dec75c..d6f6e854ebe333abc5a5ef14905c5352a3eaccdc 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt @@ -35,6 +35,7 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor( } val routeToScreen = SingleLiveEvent<NavDirections?>() + val errorEvent = SingleLiveEvent<Throwable>() val showRedeemedTokenWarning = SingleLiveEvent<Unit>() val consentGiven = submissionRepository.testForType(type = testType).map { @@ -103,9 +104,13 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor( routeToScreen.postValue(null) } - fun refreshDeviceUIState() = launch { - Timber.v("refreshDeviceUIState()") - submissionRepository.refreshTest(type = testType) + fun updateTestResult() = launch { + Timber.v("updateTestResult()") + try { + submissionRepository.refreshTest(type = testType) + } catch (e: Exception) { + errorEvent.postValue(e) + } } fun onConsentClicked() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt index 8c6448f7b5684357020b97961cdf31bee20c28e2..5ac4b198130999fcf14501a6e81fcf7b63caaf09 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt @@ -43,6 +43,8 @@ class SubmissionTestResultNoConsentFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.onTestOpened() + val backCallback = object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { showCancelDialog() @@ -70,7 +72,6 @@ class SubmissionTestResultNoConsentFragment : override fun onResume() { super.onResume() appShortcutsHelper.removeAppShortcut() - viewModel.onTestOpened() binding.submissionTestResultContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt index c558190977261129100283f195602654df04c75f..09cffe765938860b3133b292988fee6779e76405 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt @@ -48,7 +48,7 @@ class SubmissionDispatcherViewModel @AssistedInject constructor( val event = if (ratProfileSettings.profile.value != null) { SubmissionNavigationEvents.NavigateToOpenProfile } else { - SubmissionNavigationEvents.NavigateToCreateProfile + SubmissionNavigationEvents.NavigateToCreateProfile(ratProfileSettings.onboarded.value) } routeToScreen.postValue(event) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt index c9efab9912d83ee93da4bdb596965750dee6cbef..789a64d889cf8358ceb601e7694cb881073c22b3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt @@ -21,9 +21,11 @@ sealed class SubmissionNavigationEvents { val coronaTestQRCode: CoronaTestQRCode, val consentGiven: Boolean ) : SubmissionNavigationEvents() + data class NavigateToDeletionWarningFragmentFromTan(val coronaTestTan: CoronaTestTAN, val consentGiven: Boolean) : SubmissionNavigationEvents() - object NavigateToCreateProfile : SubmissionNavigationEvents() + + data class NavigateToCreateProfile(val onboarded: Boolean = false) : SubmissionNavigationEvents() object NavigateToOpenProfile : SubmissionNavigationEvents() data class ResolvePlayServicesException(val exception: ApiException) : SubmissionNavigationEvents() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt index 14ac3802e44d3c17a221821e099d3454342ecfff..e17e0f40bc990d12b73b937d8e643674e4d76445 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt @@ -114,15 +114,13 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModel @AssistedInject con fun onConsentButtonClicked() = launch { showKeysRetrievalProgress.postValue(true) submissionRepository.giveConsentToSubmission(type = testType) - launch { - if (enfClient.isTracingEnabled.first()) { - Timber.tag(TAG).d("tekHistoryUpdater.updateTEKHistoryOrRequestPermission()") - tekHistoryUpdater.updateTEKHistoryOrRequestPermission() - } else { - Timber.tag(TAG).d("showEnableTracingEvent:Unit") - showKeysRetrievalProgress.postValue(false) - showEnableTracingEvent.postValue(Unit) - } + if (enfClient.isTracingEnabled.first()) { + Timber.tag(TAG).d("tekHistoryUpdater.updateTEKHistoryOrRequestPermission()") + tekHistoryUpdater.updateTEKHistoryOrRequestPermission() + } else { + Timber.tag(TAG).d("showEnableTracingEvent:Unit") + showKeysRetrievalProgress.postValue(false) + showEnableTracingEvent.postValue(Unit) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentFragment.kt index 093eddf09783b70c5be5db58d9b7e09a2fc7687b..6fbff88ab60419f4f7ff9ddd8ec708d5c73736a6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentFragment.kt @@ -6,6 +6,7 @@ import android.view.accessibility.AccessibilityEvent import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder import de.rki.coronawarnapp.databinding.FragmentSubmissionYourConsentBinding import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.util.di.AutoInject @@ -37,13 +38,15 @@ class SubmissionYourConsentFragment : Fragment(R.layout.fragment_submission_your super.onViewCreated(view, savedInstanceState) vm.consent.observe2(this) { - binding.submissionYourConsentSwitch.status = it - binding.submissionYourConsentSwitch.statusText = getString( - if (it) { - R.string.submission_your_consent_switch_status_on - } else { - R.string.submission_your_consent_switch_status_off - } + binding.submissionYourConsentSwitch.setChecked(it) + binding.submissionYourConsentSwitch.setSubtitle( + getString( + if (it) { + R.string.submission_your_consent_switch_status_on + } else { + R.string.submission_your_consent_switch_status_off + } + ) ) } @@ -63,11 +66,7 @@ class SubmissionYourConsentFragment : Fragment(R.layout.fragment_submission_your binding.apply { submissionYourConsentTitle.headerButtonBack.buttonIcon.setOnClickListener { vm.goBack() } - submissionYourConsentSwitch.settingsSwitchRowSwitch.setOnCheckedChangeListener { view, _ -> - if (!view.isPressed) return@setOnCheckedChangeListener - vm.switchConsent() - } - submissionYourConsentSwitch.settingsSwitchRow.setOnClickListener { vm.switchConsent() } + submissionYourConsentSwitch.setOnClickListener { vm.switchConsent() } submissionYourConsentAgreementDetailsText.setOnClickListener { vm.goLegal() } submissionYourConsentAgreementShareSymptomsText.setText( @@ -78,6 +77,10 @@ class SubmissionYourConsentFragment : Fragment(R.layout.fragment_submission_your } ) } + + vm.errorEvent.observe2(this) { + it.toErrorDialogBuilder(requireContext()).show() + } } override fun onResume() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt index 13788225fdd54b616c69c5dfd8e46ec3ccd54a3c..826af6f621028d7e8260849191993a50fada4a5a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt @@ -28,6 +28,7 @@ class SubmissionYourConsentViewModel @AssistedInject constructor( } val clickEvent: SingleLiveEvent<SubmissionYourConsentEvents> = SingleLiveEvent() + val errorEvent = SingleLiveEvent<Throwable>() private val consentFlow = submissionRepository.testForType(type = testType) .filterNotNull() .map { it.isAdvancedConsentGiven } @@ -41,12 +42,17 @@ class SubmissionYourConsentViewModel @AssistedInject constructor( } fun switchConsent() = launch { - if (consentFlow.first()) { - Timber.v("revokeConsentToSubmission()") - submissionRepository.revokeConsentToSubmission(type = testType) - } else { - Timber.v("giveConsentToSubmission()") - submissionRepository.giveConsentToSubmission(type = testType) + try { + if (consentFlow.first()) { + Timber.v("revokeConsentToSubmission()") + submissionRepository.revokeConsentToSubmission(type = testType) + } else { + Timber.v("giveConsentToSubmission()") + submissionRepository.giveConsentToSubmission(type = testType) + } + } catch (e: Exception) { + Timber.e(e, "switchConsent()") + errorEvent.postValue(e) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/MoreInformationView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/MoreInformationView.kt new file mode 100644 index 0000000000000000000000000000000000000000..89c4cafbdd1fce3d8a208424517f2b3b29e67536 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/MoreInformationView.kt @@ -0,0 +1,153 @@ +package de.rki.coronawarnapp.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.util.TypedValue +import android.view.LayoutInflater +import android.widget.Checkable +import androidx.annotation.StringRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.withStyledAttributes +import androidx.core.view.isGone +import androidx.core.view.isVisible +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.ViewMoreInformationBinding + +class MoreInformationView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr), Checkable { + + private val binding: ViewMoreInformationBinding + private var onToggle: ((MoreInformationView, Boolean) -> Unit)? = null + private var toggleOnText: String? = null + private var toggleOffText: String? = null + + init { + LayoutInflater.from(context).inflate(R.layout.view_more_information, this, true) + + val outValue = TypedValue() + getContext().theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) + setBackgroundResource(outValue.resourceId) + binding = ViewMoreInformationBinding.bind(this) + + context.withStyledAttributes(attrs, R.styleable.MoreInformationView) { + + val titleText = getText(R.styleable.MoreInformationView_titleText) ?: "" + val subtitleText = getText(R.styleable.MoreInformationView_subtitleText) ?: "" + val isTopDividerVisible = getBoolean(R.styleable.MoreInformationView_isTopDividerVisible, true) + val isBottomDividerVisible = getBoolean(R.styleable.MoreInformationView_isBottomDividerVisible, true) + val isToggleVisible = getBoolean(R.styleable.MoreInformationView_isToggleVisible, false) + val isChecked = getBoolean(R.styleable.MoreInformationView_android_checked, false) + + toggleOnText = getText(R.styleable.MoreInformationView_toggleOnText)?.toString() + toggleOffText = getText(R.styleable.MoreInformationView_toggleOffText)?.toString() + + binding.apply { + topDivider.isVisible = isTopDividerVisible + bottomDivider.isVisible = isBottomDividerVisible + } + + binding.apply { + setTitle(titleText.toString()) + setSubtitle(subtitleText.toString()) + } + + binding.toggle.apply { + isGone = !isToggleVisible + isFocusable = false + isClickable = false + } + setChecked(isChecked, notify = false) + } + updateContentDescription() + } + + private fun updateContentDescription() { + val title = binding.titleElement.text + val subtitle = binding.subtitleElement.text + val isToggleVisible = binding.toggle.isVisible + contentDescription = if (isToggleVisible) { + "$title $subtitle $isChecked" + } else { + "$title $subtitle" + } + } + + fun setTitle(@StringRes stringRes: Int) { + setTitle(context.getString(stringRes)) + } + + fun setTitle(title: String) { + binding.titleElement.apply { + text = title + isVisible = title.isNotEmpty() + } + updateContentDescription() + } + + fun setSubtitle(@StringRes stringRes: Int) { + setSubtitle(context.getString(stringRes)) + } + + fun setSubtitle(subtitle: String) { + binding.subtitleElement.apply { + text = subtitle + isVisible = subtitle.isNotEmpty() + } + updateContentDescription() + } + + override fun setChecked(checked: Boolean) { + setChecked(checked, notify = false) + } + + fun setChecked(checked: Boolean, notify: Boolean) { + if (!binding.toggle.isVisible) return + val before = isChecked + + binding.toggle.isChecked = checked + + toggleOnText?.let { onText -> + toggleOffText?.let { offText -> + setSubtitle(if (isChecked) onText else offText) + } + } + + updateContentDescription() + + if (before != isChecked && notify) onToggle?.invoke(this, isChecked) + } + + override fun isChecked(): Boolean = binding.toggle.isChecked + + override fun toggle() { + isChecked = !isChecked + } + + override fun setEnabled(enabled: Boolean) { + super.setEnabled(enabled) + binding.toggle.isEnabled = enabled + } + + override fun setOnClickListener(userListener: OnClickListener?) { + if (userListener == null) { + super.setOnClickListener(null) + } else { + super.setOnClickListener { + setChecked(checked = !isChecked, notify = true) + userListener.onClick(this) + } + } + } + + fun setUserToggleListener(onToggle: ((MoreInformationView, Boolean) -> Unit)?) { + this.onToggle = onToggle + if (!hasOnClickListeners()) { + super.setOnClickListener { + setChecked(checked = !isChecked, notify = true) + } + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/SwitchRowView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/SwitchRowView.kt new file mode 100644 index 0000000000000000000000000000000000000000..901d0b0705b8a04a4539ec335c90f50c89dbd63e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/SwitchRowView.kt @@ -0,0 +1,43 @@ +package de.rki.coronawarnapp.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import com.google.android.material.switchmaterial.SwitchMaterial +import de.rki.coronawarnapp.R + +class SwitchRowView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val switch by lazy { findViewById<SwitchMaterial>(R.id.switch_view) } + private val title by lazy { findViewById<TextView>(R.id.switch_title) } + private val subtitleView by lazy { findViewById<TextView>(R.id.switch_subtitle) } + + init { + LayoutInflater.from(context).inflate(R.layout.switch_row, this, true) + } + + fun setChecked(turnedOn: Boolean?) { + switch.isChecked = turnedOn ?: false + } + + fun setTitle(text: String?) { + title.text = text + } + + fun setSubtitle(text: String?) { + subtitleView.text = text + } + + fun setSwitchEnabled(isEnabled: Boolean?) { + switch.isEnabled = isEnabled ?: false + } + + val isChecked: Boolean + get() = switch.isChecked +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt index 246ca1e5acfd8991fec7e8cb07c8e36d1d680584..f169b9c7bfb5fc48256d23d9680351c10a8360ab 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt @@ -5,6 +5,7 @@ import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest +import android.os.Build import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report @@ -76,9 +77,17 @@ object ConnectivityHelper { */ fun isNetworkEnabled(context: Context): Boolean { val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val activeNetwork: Network? = manager.activeNetwork - val caps: NetworkCapabilities? = manager.getNetworkCapabilities(activeNetwork) - return caps?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false + return when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { + val activeNetwork = manager.activeNetwork + val caps: NetworkCapabilities? = manager.getNetworkCapabilities(activeNetwork) + caps?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false + } + else -> { + val activeNetworkInfo = manager.activeNetworkInfo + activeNetworkInfo != null && activeNetworkInfo.isConnected + } + } } /** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt index 6bcdf97ff9844aa616c2b6b62c890a3447cdd9bd..cf09b33fcaddd95825f7c2c334f7d49e47b384fd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt @@ -7,16 +7,17 @@ import de.rki.coronawarnapp.bugreporting.BugReportingSettings import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings import de.rki.coronawarnapp.datadonation.analytics.Analytics import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings import de.rki.coronawarnapp.datadonation.survey.SurveySettings import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository +import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.presencetracing.TraceLocationSettings import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository -import de.rki.coronawarnapp.main.CWASettings -import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.presencetracing.warning.storage.TraceWarningRepository import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.statistics.source.StatisticsProvider @@ -62,6 +63,7 @@ class DataReset @Inject constructor( private val traceLocationSettings: TraceLocationSettings, private val traceWarningRepository: TraceWarningRepository, private val coronaTestRepository: CoronaTestRepository, + private val ratProfileSettings: RATProfileSettings ) { private val mutex = Mutex() @@ -104,6 +106,7 @@ class DataReset @Inject constructor( traceLocationRepository.deleteAllTraceLocations() checkInRepository.clear() coronaTestRepository.clear() + ratProfileSettings.deleteProfile() Timber.w("CWA LOCAL DATA DELETION COMPLETED.") } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DialogHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DialogHelper.kt index dedb9a57f4c54549df0e26449725614703ca67b5..9688f3585512c7c0e54bafe7476c1863d581b0a3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DialogHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DialogHelper.kt @@ -6,6 +6,7 @@ import android.text.method.LinkMovementMethod import android.text.util.Linkify import android.widget.TextView import androidx.appcompat.app.AlertDialog +import androidx.core.widget.TextViewCompat import de.rki.coronawarnapp.R import de.rki.coronawarnapp.util.ContextExtensions.getColorStateListCompat import java.util.regex.Pattern @@ -111,7 +112,7 @@ object DialogHelper { textView.linksClickable = true textView.movementMethod = LinkMovementMethod.getInstance() textView.setPadding(paddingStartEnd, paddingLeftRight, paddingStartEnd, paddingLeftRight) - textView.setTextAppearance(R.style.body1) + TextViewCompat.setTextAppearance(textView, R.style.body1) textView.setLinkTextColor(context.getColorStateListCompat(R.color.button_primary)) if (isTextSelectable) textView.setTextIsSelectable(true) return textView diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt index 61c9b2698cb6e8d8a9177579ac5b843890111c94..576047ca22cf24f014ac205a98a159576a453bd9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt @@ -1,10 +1,40 @@ package de.rki.coronawarnapp.util +import android.text.Spannable import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.URLSpan +import androidx.core.text.toSpannable fun SpannableStringBuilder.urlSpan(start: Int, end: Int, value: String): SpannableStringBuilder { setSpan(URLSpan(value), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) return this } + +/** + * Creates a [Spannable] from all the elements separated using [separator] + * and using the given [prefix] and [postfix] if supplied. + * + * If the collection could be huge, you can specify a non-negative value of [limit], + * in which case only the first [limit] + * elements will be appended, followed by the [truncated] string (which defaults to "..."). + **/ +@Suppress("LongParameterList") +fun <T> Iterable<T>.joinToSpannable( + separator: CharSequence = ", ", + prefix: CharSequence = "", + postfix: CharSequence = "", + limit: Int = -1, + truncated: CharSequence = "...", + transform: ((T) -> CharSequence)? = null +): Spannable { + return joinTo( + SpannableStringBuilder(), + separator, + prefix, + postfix, + limit, + truncated, + transform + ).toSpannable() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/PowerManagement.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/PowerManagement.kt index f022f72dd505659514f3e27b62cbb5b3442da731..294f10ec05df02679174831951e5a56e29b04b7a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/PowerManagement.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/device/PowerManagement.kt @@ -2,9 +2,10 @@ package de.rki.coronawarnapp.util.device import android.content.Context import android.content.Intent -import android.net.Uri +import android.os.Build import android.os.PowerManager import android.provider.Settings +import androidx.annotation.RequiresApi import androidx.core.content.getSystemService import de.rki.coronawarnapp.util.di.AppContext import javax.inject.Inject @@ -18,14 +19,13 @@ class PowerManagement @Inject constructor( private val powerManager by lazy { context.getSystemService<PowerManager>()!! } val isIgnoringBatteryOptimizations - get() = powerManager.isIgnoringBatteryOptimizations(context.packageName) + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + powerManager.isIgnoringBatteryOptimizations(context.packageName) + } else { + true + } + @RequiresApi(Build.VERSION_CODES.M) val toBatteryOptimizationSettingsIntent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS) - - val disableBatteryOptimizationsIntent = - Intent( - Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, - Uri.parse("package:${context.packageName}") - ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt index 574ed5dbcdd248c658dc375b0b2b0539adfd76f3..1728ab9379762422fc2400609eeb2de1c65cb24f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.util.di import dagger.BindsInstance import dagger.Component +import dagger.Lazy import dagger.android.AndroidInjector import dagger.android.support.AndroidSupportInjectionModule import de.rki.coronawarnapp.CoronaWarnApplication @@ -102,7 +103,7 @@ interface ApplicationComponent : AndroidInjector<CoronaWarnApplication> { fun inject(logger: DebugLogger) - val encryptedMigration: EncryptedPreferencesMigration + val encryptedMigration: Lazy<EncryptedPreferencesMigration> @Component.Factory interface Factory { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/network/NetworkStateProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/network/NetworkStateProvider.kt index 7f06ba7a369d8cbc9150673110c5004f552c504b..e74c06d654b8431e4692e9e214b37f8c91a297e2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/network/NetworkStateProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/network/NetworkStateProvider.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.util.network +import android.annotation.SuppressLint import android.content.Context import android.net.ConnectivityManager import android.net.LinkProperties @@ -8,6 +9,8 @@ import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.net.ConnectivityManagerCompat import de.rki.coronawarnapp.storage.TestSettings import de.rki.coronawarnapp.util.BuildVersionWrap import de.rki.coronawarnapp.util.coroutine.AppScope @@ -37,23 +40,26 @@ class NetworkStateProvider @Inject constructor( val networkState: Flow<State> = callbackFlow { send(currentState) - val callback = object : ConnectivityManager.NetworkCallback() { - override fun onAvailable(network: Network) { - Timber.tag(TAG).v("onAvailable(network=%s)", network) - appScope.launch { send(currentState) } - } - - override fun onUnavailable() { - Timber.tag(TAG).v("onUnavailable()") - appScope.launch { send(currentState) } - } - } val request = networkRequestBuilderProvider.get() .addCapability(NET_CAPABILITY_INTERNET) .build() + var registeredCallback: ConnectivityManager.NetworkCallback? = null + try { + val callback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + Timber.tag(TAG).v("onAvailable(network=%s)", network) + appScope.launch { send(currentState) } + } + + override fun onUnavailable() { + Timber.tag(TAG).v("onUnavailable()") + appScope.launch { send(currentState) } + } + } + /** * This may throw java.lang.SecurityException on Samsung devices * java.lang.SecurityException: @@ -61,6 +67,7 @@ class NetworkStateProvider @Inject constructor( * at android.net.ConnectivityManager.registerNetworkCallback (ConnectivityManager.java:4564) */ manager.registerNetworkCallback(request, callback) + registeredCallback = callback } catch (e: SecurityException) { Timber.e(e, "registerNetworkCallback() threw an undocumented SecurityException, Just Samsung Thingsâ„¢ï¸") State( @@ -79,8 +86,8 @@ class NetworkStateProvider @Inject constructor( } awaitClose { - Timber.tag(TAG).v("unregisterNetworkCallback()") - manager.unregisterNetworkCallback(callback) + Timber.tag(TAG).v("unregisterNetworkCallback(%s)", registeredCallback) + registeredCallback?.let { manager.unregisterNetworkCallback(it) } fakeConnectionSubscriber.cancel() } } @@ -90,34 +97,50 @@ class NetworkStateProvider @Inject constructor( ) private val currentState: State - get() = manager.activeNetwork.let { network -> - State( - activeNetwork = network, - capabilities = network?.let { - try { - manager.getNetworkCapabilities(it) - } catch (e: SecurityException) { - Timber.tag(TAG).e(e, "Failed to determine network capabilities.") - null - } - }, - linkProperties = network?.let { - try { - manager.getLinkProperties(it) - } catch (e: Exception) { - Timber.tag(TAG).e(e, "Failed to determine link properties.") - null - } - }, - isFakeMeteredConnection = testSettings.fakeMeteredConnection.value - ) + @SuppressLint("NewApi") + get() = when { + BuildVersionWrap.hasAPILevel(Build.VERSION_CODES.M) -> api23NetworkState() + else -> { + // Most state information is not available + State( + activeNetwork = null, + capabilities = null, + linkProperties = null, + assumeMeteredConnection = testSettings.fakeMeteredConnection.value || + ConnectivityManagerCompat.isActiveNetworkMetered(manager) + ) + } } + @RequiresApi(Build.VERSION_CODES.M) + private fun api23NetworkState() = manager.activeNetwork.let { network -> + State( + activeNetwork = network, + capabilities = network?.let { + try { + manager.getNetworkCapabilities(it) + } catch (e: SecurityException) { + Timber.tag(TAG).e(e, "Failed to determine network capabilities.") + null + } + }, + linkProperties = network?.let { + try { + manager.getLinkProperties(it) + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to determine link properties.") + null + } + }, + assumeMeteredConnection = testSettings.fakeMeteredConnection.value + ) + } + data class State( val activeNetwork: Network?, val capabilities: NetworkCapabilities?, val linkProperties: LinkProperties?, - private val isFakeMeteredConnection: Boolean = false + private val assumeMeteredConnection: Boolean = false ) { val isMeteredConnection: Boolean get() { @@ -126,7 +149,7 @@ class NetworkStateProvider @Inject constructor( } else { capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false } - return isFakeMeteredConnection || !unMetered + return assumeMeteredConnection || !unMetered } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt deleted file mode 100644 index d598cdf5b08af8ac134ffcedd0524206332a9229..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt +++ /dev/null @@ -1,57 +0,0 @@ -package de.rki.coronawarnapp.util.ui - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -fun <T : Any> ViewModel.smartLiveData( - dispatcher: CoroutineDispatcher = Dispatchers.Default, - liveDataFactory: (ViewModel, CoroutineDispatcher) -> SmartLiveData<T> = { vm, disp -> - SmartLiveData(vm, disp) - }, - initAction: suspend () -> T -) = SmartLiveDataProperty(dispatcher, initAction, liveDataFactory) - -class SmartLiveDataProperty<T : Any, LV : SmartLiveData<T>>( - private val dispatcher: CoroutineDispatcher = Dispatchers.Default, - private val initialValueProvider: suspend () -> T, - private val liveDataFactory: (ViewModel, CoroutineDispatcher) -> LV -) : ReadOnlyProperty<ViewModel, SmartLiveData<T>> { - - private var liveData: SmartLiveData<T>? = null - - override fun getValue( - thisRef: ViewModel, - property: KProperty<*> - ): SmartLiveData<T> { - liveData?.let { - return@getValue it - } - - return liveDataFactory(thisRef, dispatcher).also { - liveData = it - thisRef.viewModelScope.launch(context = dispatcher) { - it.postValue(initialValueProvider()) - } - } - } -} - -open class SmartLiveData<T : Any>( - private val viewModel: ViewModel, - private val dispatcher: CoroutineDispatcher -) : MutableLiveData<T>() { - - fun update(updateAction: (T) -> T) { - observeOnce { - viewModel.viewModelScope.launch(context = dispatcher) { - postValue(updateAction(it)) - } - } - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/views/MoreInformationView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/views/MoreInformationView.kt deleted file mode 100644 index a36e2c0ba31cd4bc69328077c0aac71246b82ecf..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/views/MoreInformationView.kt +++ /dev/null @@ -1,49 +0,0 @@ -package de.rki.coronawarnapp.util.ui.views - -import android.content.Context -import android.util.AttributeSet -import android.util.TypedValue -import android.view.LayoutInflater -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.withStyledAttributes -import androidx.core.view.isVisible -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.ViewMoreInformationBinding - -class MoreInformationView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr) { - - private val binding: ViewMoreInformationBinding - - init { - LayoutInflater.from(context).inflate(R.layout.view_more_information, this, true) - - val outValue = TypedValue() - getContext().theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) - setBackgroundResource(outValue.resourceId) - binding = ViewMoreInformationBinding.bind(this) - - context.withStyledAttributes(attrs, R.styleable.MoreInformationView) { - - val titleText = getText(R.styleable.MoreInformationView_titleText) ?: "" - val subtitleText = getText(R.styleable.MoreInformationView_subtitleText) ?: "" - val isTopDividerVisible = getBoolean(R.styleable.MoreInformationView_isTopDividerVisible, true) - val isBottomDividerVisible = getBoolean(R.styleable.MoreInformationView_isBottomDividerVisible, true) - - binding.apply { - - topDivider.isVisible = isTopDividerVisible - bottomDivider.isVisible = isBottomDividerVisible - - moreInformationTitle.text = titleText - moreInformationTitle.isVisible = titleText.isNotEmpty() - - moreInformationSubtitle.text = subtitleText - moreInformationSubtitle.isVisible = subtitleText.isNotEmpty() - } - } - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt index 32bec142a340b955e949c5b6b58fd1b7e79fe838..600b8a8f2a785fe5213d091972798960eeb9869b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt @@ -19,13 +19,6 @@ object BackgroundConstants { */ const val WORKER_RETRY_COUNT_THRESHOLD = 2 - /** - * The maximum validity in days for keeping Background polling active - * - * @see TimeUnit.DAYS - */ - const val POLLING_VALIDITY_MAX_DAYS = 21 - /** * Backoff initial delay * diff --git a/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit.xml b/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit.xml new file mode 100644 index 0000000000000000000000000000000000000000..f748bc904ab4d9af7924f7f230496403983150ba --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorSurface2" /> + </shape> + </item> + <item + android:height="@dimen/submission_tan_input_digit_stroke" + android:gravity="bottom"> + <shape android:shape="rectangle"> + <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> + <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorTextSemanticNeutral" /> + </shape> + </item> +</layer-list> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit_error.xml b/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit_error.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e293e04fc5f65d2ea027dfa86effb787eb5a4b0 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-v23/tan_input_digit_error.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorSurface2" /> + </shape> + </item> + <item + android:height="@dimen/submission_tan_input_digit_stroke" + android:gravity="bottom"> + <shape android:shape="rectangle"> + <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> + <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorTextSemanticRed" /> + </shape> + </item> +</layer-list> diff --git a/Corona-Warn-App/src/main/res/drawable/rat_profile_gradient.xml b/Corona-Warn-App/src/main/res/drawable/rat_profile_gradient.xml new file mode 100644 index 0000000000000000000000000000000000000000..85fdf590f418b7008036246cbbc244b07b29de9a --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/rat_profile_gradient.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:angle="135" + android:endColor="#599BC4" + android:startColor="#29689B" /> +</shape> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/rat_profile_onboarding.xml b/Corona-Warn-App/src/main/res/drawable/rat_profile_onboarding.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d103e9ec8026e4163260d447bc58602f6ce7321 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/rat_profile_onboarding.xml @@ -0,0 +1,169 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="245dp" + android:height="147dp" + android:viewportWidth="245" + android:viewportHeight="147"> + <path + android:pathData="M25.795,29.373C15.224,62.399 55.133,61.993 40.105,87.512C25.556,112.161 46.814,115.021 46.814,115.021L220.769,115.842C220.769,115.842 243.618,78.526 237.666,50.321C233.056,26.31 202.712,10.3 186.965,18.435C146.079,39.432 123.939,12.474 103.543,6.609C64.133,-4.667 35.332,-0.406 25.795,29.373Z" + android:fillColor="#E9E9E9" + android:fillType="evenOdd"/> + <path + android:pathData="M87.052,29.985L86.368,36.867L84.976,37.978L83.584,38.811L85.532,42.975L96.39,43.252L94.998,38.811L95.276,36.59L94.427,36.218C93.745,35.918 93.266,35.288 93.166,34.549L92.545,29.982H87.052V29.985Z" + android:fillColor="#F6B893"/> + <path + android:pathData="M95.81,25.163C95.234,36.389 84,34.379 84.276,23.103C84.85,11.876 96.086,13.883 95.81,25.163Z" + android:fillColor="#F6B893"/> + <path + android:strokeWidth="1" + android:pathData="M95.053,26.312C92.406,27.158 89.381,27.898 83.71,26.312C82.922,27.193 82.576,29.8 85.6,31.068C88.625,32.336 92.217,31.597 93.635,31.068C95.21,29.131 97.699,25.467 95.053,26.312Z" + android:fillColor="#ffffff" + android:strokeColor="#ffffff"/> + <path + android:pathData="M81.356,68.237C76.623,87.115 81.166,114.241 82.631,136.011L86.066,136.122C86.066,136.122 88.037,100.718 88.872,92.667C89.126,90.222 89.955,84.994 90.379,82.72C90.412,82.548 90.777,81.796 90.821,82.673C91.1,88.226 93.605,94.333 94.162,100.441C95.462,114.713 95.176,124.649 95.741,136.428C95.741,136.428 99.725,138.776 99.73,138.751C100.563,135.465 102.793,108.386 102.793,104.327C102.793,103.494 103.879,86.152 99.452,68.515L81.356,68.237Z" + android:fillColor="#8C8C98"/> + <path + android:pathData="M104.068,69.506C101.454,79.058 115.366,81.685 105.956,67.604L104.068,69.506Z" + android:fillColor="#F6B893"/> + <path + android:pathData="M72.249,62.962C74.337,66.293 76.155,64.511 81.158,61.018C81.397,60.852 81.636,60.677 81.884,60.502C81.806,63.855 81.65,66.879 81.358,68.236C80.684,71.376 100.29,71.29 99.733,69.069C98.552,64.366 98.062,55.744 98.062,49.081C98.062,48.526 98.992,51.324 99.454,53.245C100.629,58.112 101.484,65.199 103.333,69.208C103.909,70.457 104.727,69.938 106.08,69.472C107.239,69.058 106.771,67.109 106.735,66.623C106.473,63.109 105.858,58.189 105.02,54.353C103.734,48.448 102.765,39.692 95.273,36.433C95.15,36.38 95.008,36.472 95.011,36.608C95.084,39.098 92.436,42.693 91.1,42.693C88.594,42.693 84.418,39.084 83.839,36.169C77.878,37.904 76.107,44.656 74.952,48.8C73.56,53.8 71.174,61.249 72.249,62.962ZM79.045,56.449C79.239,55.968 79.601,54.927 80.244,52.69L81.567,48.079C81.625,47.873 81.929,47.912 81.932,48.126C81.948,49.725 82.255,51.188 82.232,54.353C80.834,55.486 79.757,56.515 79.281,56.69C79.128,56.749 78.983,56.599 79.045,56.449Z" + android:fillColor="#C66A61"/> + <path + android:pathData="M86.163,134.455L82.469,134.033C82.363,134.982 81.077,136.531 81.787,140.215C81.843,140.509 81.912,143.311 82.007,143.613C82.093,143.894 83.304,144.302 83.304,144.024C83.304,143.744 82.959,140.285 83.583,141.526C83.861,142.081 83.861,144.024 85.253,145.135C86.843,146.403 91.851,146.811 89.986,144.579C87.202,141.248 86.369,135.965 86.152,134.799" + android:fillColor="#4A4A4A"/> + <path + android:pathData="M95.699,134.638C95.699,134.638 95.64,136.47 95.275,137.919C94.997,139.03 94.719,142.361 95.832,145.137H97.503C97.503,145.137 96.946,142.083 97.224,140.418C97.299,139.973 97.503,142.361 99.173,144.304C99.941,145.198 100.632,145.32 100.958,145.356C101.556,145.423 105.109,145.415 105.298,145.415C105.855,145.415 106.412,144.304 104.176,143.319C103.577,143.055 102.831,142.55 102.236,141.806C101.222,140.543 100.757,138.405 100.2,135.351L95.699,134.638Z" + android:fillColor="#4A4A4A"/> + <path + android:pathData="M86.632,53.348L87.768,54.193C92.674,52.461 95.459,43.756 88.739,47.374C87.199,48.846 86.425,50.864 85.557,52.429" + android:fillColor="#F6B893"/> + <path + android:pathData="M97.823,37.919C97.43,37.622 97.107,37.405 96.667,37.147C96.158,36.847 95.799,36.683 95.236,36.447C95.192,36.428 95.089,36.447 95.058,36.506C94.585,36.389 94.209,36.178 93.816,35.814C92.99,35.048 92.461,33.688 92.739,32.3C92.889,31.55 93.179,31.239 93.338,31.048C96.915,26.748 94.56,19.904 94.56,19.904C94.56,19.904 92.55,24.13 84.966,27.022C84.966,27.022 85.281,28.988 86.394,30.376C86.475,30.479 87.31,31.231 87.341,32.411C87.369,33.569 87.371,34.74 86.578,36.514C85.612,38.679 83.154,39.201 81.288,38.457C79.896,37.902 79.339,36.514 79.275,35.145C79.275,35.145 83.07,35.368 82.176,31.142C81.508,27.966 80.712,21.029 83.855,17.017C83.9,16.962 83.944,16.906 83.989,16.851C84.019,16.809 84.056,16.767 84.092,16.728C84.156,16.653 84.223,16.579 84.289,16.506C84.354,16.44 84.418,16.373 84.482,16.306C85.467,15.31 86.765,14.56 88.46,14.194C88.46,14.194 94.93,13.305 96.044,17.087C96.044,17.087 97.828,17.978 98.054,21.315C98.277,24.652 97.717,25.873 97.606,28.433C97.472,31.545 97.383,34.662 100.729,35.329C100.735,35.323 100.852,37.022 97.823,37.919Z" + android:fillColor="#4A4A4A"/> + <group> + <clip-path + android:pathData="M141,14.116h103.79v104.24h-103.79z"/> + <path + android:pathData="M239.57,29.976H182.35V118.356H239.57V29.976Z" + android:fillColor="#94A1AB"/> + <path + android:strokeWidth="1" + android:pathData="M206.2,63.406H192.67C191.991,63.406 191.44,63.957 191.44,64.636V76.166C191.44,76.846 191.991,77.396 192.67,77.396H206.2C206.879,77.396 207.43,76.846 207.43,76.166V64.636C207.43,63.957 206.879,63.406 206.2,63.406Z" + android:fillColor="#E3F0FF" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M199.44,63.586V77.296" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M206.98,70.296H191.72" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M229.08,63.306H215.55C214.871,63.306 214.32,63.857 214.32,64.536V76.066C214.32,76.746 214.871,77.296 215.55,77.296H229.08C229.759,77.296 230.31,76.746 230.31,76.066V64.536C230.31,63.857 229.759,63.306 229.08,63.306Z" + android:fillColor="#E3F0FF" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M222.32,63.486V77.186" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M229.86,70.196H214.6" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M206.28,39.776H192.75C192.071,39.776 191.52,40.327 191.52,41.006V52.536C191.52,53.216 192.071,53.766 192.75,53.766H206.28C206.959,53.766 207.51,53.216 207.51,52.536V41.006C207.51,40.327 206.959,39.776 206.28,39.776Z" + android:fillColor="#E3F0FF" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M199.51,39.956V53.666" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M207.06,46.666H191.79" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M229.16,39.666H215.63C214.951,39.666 214.4,40.217 214.4,40.896V52.426C214.4,53.106 214.951,53.656 215.63,53.656H229.16C229.839,53.656 230.39,53.106 230.39,52.426V40.896C230.39,40.217 229.839,39.666 229.16,39.666Z" + android:fillColor="#E3F0FF" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M222.4,39.846V53.556" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:strokeWidth="1" + android:pathData="M229.94,46.556H214.68" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:pathData="M186.63,117.116C188.29,115.266 189.47,112.376 188.52,106.366C187.57,100.356 186.79,98.986 185.29,98.746C183.79,98.506 182.84,99.466 182.45,101.556C182.06,103.646 182.13,105.726 181.45,106.556C180.77,107.386 177.19,111.846 179.24,115.696C181.29,119.546 184.77,118.506 186.58,117.146" + android:fillColor="#B8E0FA" + android:fillType="evenOdd"/> + <path + android:pathData="M182.47,118.356C183.494,115.729 184.252,113.005 184.73,110.226C185.206,107.814 185.387,105.352 185.27,102.896H185.08C185.189,105.336 185.004,107.78 184.53,110.176C184.057,112.941 183.307,115.652 182.29,118.266L182.47,118.356Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M184.11,112.976C184,113.116 186.9,111.206 187.45,109.316L187.26,109.256C186.73,111.076 184.12,112.736 184.11,112.736V112.976Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M183.2,116.416C182.285,115.179 181.606,113.782 181.2,112.296H181.03C181.423,113.814 182.102,115.243 183.03,116.506L183.2,116.416Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M176.79,87.476C175.13,87.476 174.18,89.876 174.1,90.996C173.77,95.796 170.83,99.216 171.1,106.686C171.27,111.586 172.99,116.616 174.8,117.496C178.3,119.186 180.64,115.416 181.04,111.886C181.696,106.797 181.006,101.626 179.04,96.886C177.59,93.326 180.04,87.436 176.75,87.516" + android:fillColor="#B8E0FA" + android:fillType="evenOdd"/> + <path + android:pathData="M176.66,118.116C175.956,113.078 175.621,107.994 175.66,102.906C175.599,98.655 175.934,94.406 176.66,90.216H176.85C176.129,94.397 175.794,98.635 175.85,102.876C175.811,107.951 176.145,113.021 176.85,118.046L176.66,118.116Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M175.84,109.726C176.886,108.054 177.77,106.286 178.48,104.446H178.67C177.957,106.319 177.063,108.118 176,109.816L175.84,109.726Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M175.66,101.246C175.66,101.246 173.66,98.906 173.82,97.166H174C173.88,98.846 175.79,101.116 175.79,101.116L175.66,101.246Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M242.23,21.446H179.69C178.276,21.446 177.13,22.592 177.13,24.006V28.886C177.13,30.3 178.276,31.446 179.69,31.446H242.23C243.644,31.446 244.79,30.3 244.79,28.886V24.006C244.79,22.592 243.644,21.446 242.23,21.446Z" + android:fillColor="#C36A61"/> + <path + android:pathData="M194.22,118.356V90.396H210.95V118.356" + android:fillColor="#E3F0FF"/> + <path + android:strokeWidth="1" + android:pathData="M194.22,118.356V90.396H210.95V118.356" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:pathData="M210.95,118.356V90.396H227.69V118.356" + android:fillColor="#E3F0FF"/> + <path + android:strokeWidth="1" + android:pathData="M210.95,118.356V90.396H227.69V118.356" + android:fillColor="#00000000" + android:strokeColor="#0088B2"/> + <path + android:pathData="M207.43,107.106C208.093,107.106 208.63,106.569 208.63,105.906C208.63,105.244 208.093,104.706 207.43,104.706C206.767,104.706 206.23,105.244 206.23,105.906C206.23,106.569 206.767,107.106 207.43,107.106Z" + android:fillColor="#0088B2"/> + <path + android:pathData="M215.53,107.106C216.193,107.106 216.73,106.569 216.73,105.906C216.73,105.244 216.193,104.706 215.53,104.706C214.867,104.706 214.33,105.244 214.33,105.906C214.33,106.569 214.867,107.106 215.53,107.106Z" + android:fillColor="#0088B2"/> + </group> + <path + android:pathData="M54.133,57.126C55.479,57.126 56.189,56.364 56.189,55.044V48.823C56.189,43.593 53.523,40.978 48.217,40.978H41.996C40.676,40.978 39.939,41.688 39.939,43.009C39.939,44.329 40.676,45.065 41.996,45.065H48.141C50.654,45.065 52.102,46.411 52.102,49.052V55.044C52.102,56.364 52.838,57.126 54.133,57.126ZM8.506,57.126C9.852,57.126 10.563,56.364 10.563,55.044V49.052C10.563,46.411 11.959,45.065 14.498,45.065H20.643C21.988,45.065 22.725,44.329 22.725,43.009C22.725,41.688 21.988,40.978 20.643,40.978H14.447C9.141,40.978 6.475,43.593 6.475,48.823V55.044C6.475,56.364 7.211,57.126 8.506,57.126ZM20.262,64.718H29.098C29.732,64.718 30.215,64.235 30.215,63.626V54.765C30.215,54.155 29.732,53.647 29.098,53.647H20.262C19.627,53.647 19.145,54.155 19.145,54.765V63.626C19.145,64.235 19.627,64.718 20.262,64.718ZM33.516,64.718H42.352C42.986,64.718 43.469,64.235 43.469,63.626V54.765C43.469,54.155 42.986,53.647 42.352,53.647H33.516C32.881,53.647 32.398,54.155 32.398,54.765V63.626C32.398,64.235 32.881,64.718 33.516,64.718ZM21.354,62.509V55.856H28.006V62.509H21.354ZM34.607,62.509V55.856H41.26V62.509H34.607ZM23.283,60.579H26.076V57.786H23.283V60.579ZM36.563,60.579H39.33V57.786H36.563V60.579ZM20.262,77.997H29.098C29.732,77.997 30.215,77.515 30.215,76.88V68.044C30.215,67.409 29.732,66.927 29.098,66.927H20.262C19.627,66.927 19.145,67.409 19.145,68.044V76.88C19.145,77.515 19.627,77.997 20.262,77.997ZM32.729,69.999H35.496V67.231H32.729V69.999ZM40.371,69.999H43.139V67.231H40.371V69.999ZM21.354,75.788V69.136H28.006V75.788H21.354ZM23.283,73.833H26.076V71.065H23.283V73.833ZM36.537,73.833H39.33V71.065H36.537V73.833ZM14.447,90.667H20.643C21.988,90.667 22.725,89.931 22.725,88.636C22.725,87.315 21.988,86.579 20.643,86.579H14.498C11.959,86.579 10.563,85.233 10.563,82.593V76.601C10.563,75.255 9.826,74.519 8.506,74.519C7.186,74.519 6.475,75.255 6.475,76.601V82.796C6.475,88.052 9.141,90.667 14.447,90.667ZM41.996,90.667H48.217C53.523,90.667 56.189,88.026 56.189,82.796V76.601C56.189,75.255 55.453,74.519 54.133,74.519C52.813,74.519 52.102,75.255 52.102,76.601V82.593C52.102,85.233 50.654,86.579 48.141,86.579H41.996C40.676,86.579 39.939,87.315 39.939,88.636C39.939,89.931 40.676,90.667 41.996,90.667ZM32.729,77.667H35.496V74.899H32.729V77.667ZM40.371,77.667H43.139V74.899H40.371V77.667Z" + android:fillColor="#3E6883"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/trace_location_qr_code_background.xml b/Corona-Warn-App/src/main/res/drawable/rounded_white_background.xml similarity index 100% rename from Corona-Warn-App/src/main/res/drawable/trace_location_qr_code_background.xml rename to Corona-Warn-App/src/main/res/drawable/rounded_white_background.xml diff --git a/Corona-Warn-App/src/main/res/drawable/splash_screen.xml b/Corona-Warn-App/src/main/res/drawable/splash_screen.xml index 78d8273a08f07b4d02130ea829210814d5c962ed..f3cffa04d19eea119b6e4d3494eccd9db8a88b13 100644 --- a/Corona-Warn-App/src/main/res/drawable/splash_screen.xml +++ b/Corona-Warn-App/src/main/res/drawable/splash_screen.xml @@ -5,8 +5,10 @@ <item android:drawable="@color/colorBackground" /> <!-- logo --> - <item - android:drawable="@drawable/ic_splash_logo_round" - android:gravity="center" /> + <item> + <bitmap + android:gravity="center" + android:src="@drawable/ic_splash_logo_round" /> + </item> </layer-list> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml b/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml index f748bc904ab4d9af7924f7f230496403983150ba..8b469ff0bc7bd63b420a7dc104cba0e4ce126465 100644 --- a/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml +++ b/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml @@ -1,5 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- + This asset is only used below Android 6. These Android versions + do not support the `android:height` and `android:width` attributes, + making tricks necessary. + --> + <item> <shape android:shape="rectangle"> <corners android:radius="@dimen/submission_tan_input_digit_radius" /> @@ -7,12 +14,14 @@ </shape> </item> <item - android:height="@dimen/submission_tan_input_digit_stroke" - android:gravity="bottom"> + android:top="-4dp" + android:right="-4dp" + android:left="-4dp"> <shape android:shape="rectangle"> - <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> - <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> - <solid android:color="@color/colorTextSemanticNeutral" /> + <stroke + android:width="@dimen/submission_tan_input_digit_stroke" + android:color="@color/colorTextSemanticNeutral" + /> </shape> </item> </layer-list> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml index 8e293e04fc5f65d2ea027dfa86effb787eb5a4b0..93f55158e33f559ee3806d3944a99302fc118a8c 100644 --- a/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml +++ b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- + This asset is only used below Android 6. These Android versions + do not support the `android:height` and `android:width` attributes, + making tricks necessary. + --> + + <item> <shape android:shape="rectangle"> <corners android:radius="@dimen/submission_tan_input_digit_radius" /> @@ -7,12 +15,14 @@ </shape> </item> <item - android:height="@dimen/submission_tan_input_digit_stroke" - android:gravity="bottom"> + android:top="-4dp" + android:right="-4dp" + android:left="-4dp"> <shape android:shape="rectangle"> - <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> - <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> - <solid android:color="@color/colorTextSemanticRed" /> + <stroke + android:width="@dimen/submission_tan_input_digit_stroke" + android:color="@color/colorTextSemanticRed" + /> </shape> </item> </layer-list> diff --git a/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_fragment.xml b/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_fragment.xml index 497d4491be0c86e6c0b32196ba1d18c9b0817be6..e0d43d04f557e4124a255dbc1fb046f6e983a5fb 100644 --- a/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_fragment.xml @@ -61,7 +61,7 @@ android:layout_marginVertical="24dp" android:focusable="true" /> - <de.rki.coronawarnapp.util.ui.views.MoreInformationView + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@+id/debug_log_history_container" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -73,7 +73,7 @@ app:titleText="@string/debugging_debuglog_id_history_title" tools:visibility="visible" /> - <de.rki.coronawarnapp.util.ui.views.MoreInformationView + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@+id/debug_log_privacy_information" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_upload_fragment.xml b/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_upload_fragment.xml index 33a772ec011e4cef6115a68f58f0d2f4cd5b9960..c5b191c1283b1102487f4ab76407a21e53a44e24 100644 --- a/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_upload_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/bugreporting_debuglog_upload_fragment.xml @@ -64,7 +64,7 @@ android:layout_marginVertical="24dp" android:focusable="true" /> - <de.rki.coronawarnapp.util.ui.views.MoreInformationView + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@+id/debug_log_privacy_information" android:layout_marginHorizontal="24dp" android:layout_width="match_parent" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml b/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml index 2d648ab3d6bedc5e45b1281e73caa299ca072adc..056e4cb18b2b57ed87b826e47f18c7fa4710073d 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml @@ -34,12 +34,13 @@ android:clipToPadding="false" android:scrollbarStyle="outsideOverlay"> + <!-- Won't be translated, for that it's hardcoded --> <include android:id="@+id/information_technical_header_details" layout="@layout/include_information_details" android:layout_width="match_parent" android:layout_height="wrap_content" - app:body="@{FormatterHelper.parseHtmlFromAssets(context, @string/information_technical_html_path)}" + app:body='@{FormatterHelper.parseHtmlFromAssets(context, "technical.html")}' app:illustration="@{@drawable/ic_information_illustration_technical}" app:illustrationDescription="@{@string/information_technical_illustration_description}" /> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings.xml index 48388cf2745a58fb247bc93d6ac7184f87decde3..ff07972310cb78f3f25e503ae12dcaed739ad039 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_settings.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings.xml @@ -91,6 +91,7 @@ layout="@layout/include_setting_row" android:layout_width="0dp" android:layout_height="wrap_content" + app:gone="@{!backgroundState.showBackgroundPrioritySettings()}" app:body="@{@string/settings_background_priority_body_description}" app:color="@{backgroundState.getBackgroundPriorityIconColor(context)}" app:icon="@{backgroundState.getBackgroundPriorityIcon(context)}" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings_notifications.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings_notifications.xml index f97962270f1d8305cd082be0e9b76bbf5ce632c0..0b3690fb5d0b773ac7cc44431c31f72153670082 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_settings_notifications.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings_notifications.xml @@ -17,16 +17,15 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <include + <com.google.android.material.appbar.MaterialToolbar android:id="@+id/settings_notifications_header" - layout="@layout/include_header" + style="@style/CWAToolbar.BackArrow" android:layout_width="0dp" android:layout_height="wrap_content" - app:icon="@{@drawable/ic_back}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:title="@{@string/settings_notifications_title}" /> + app:title="@string/settings_notifications_title" /> <ScrollView android:layout_width="0dp" @@ -57,36 +56,49 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - <include + <de.rki.coronawarnapp.ui.view.SwitchRowView android:id="@+id/settings_switch_row_notifications_risk" - layout="@layout/include_settings_switch_row" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_small" gone="@{state != null && !state.isNotificationsEnabled}" - app:enabled="@{true}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/settings_notifications_header_details" - app:showDivider="@{true}" - app:status="@{state.isNotificationsRiskEnabled}" - app:statusText="@{state.getRiskNotificationStatusText(context)}" - app:subtitle="@{@string/settings_notifications_subtitle_update_risk}" /> - - <include + app:checked="@{state.isNotificationsRiskEnabled}" + app:subtitle="@{state.getRiskNotificationStatusText(context)}" + app:switchEnabled="@{true}" + app:title="@{@string/settings_notifications_subtitle_update_risk}" /> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:background="@color/colorHairline" + app:layout_constraintTop_toBottomOf="@id/settings_switch_row_notifications_risk" /> + + <de.rki.coronawarnapp.ui.view.SwitchRowView android:id="@+id/settings_switch_row_notifications_test" - layout="@layout/include_settings_switch_row" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" gone="@{ state != null && !state.isNotificationsEnabled}" - app:enabled="@{true}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/settings_switch_row_notifications_risk" - app:showDivider="@{true}" - app:status="@{state.isNotificationsTestEnabled}" - app:statusText="@{state.getTestNotificationStatusText(context)}" - app:subtitle="@{@string/settings_notifications_subtitle_update_test}" /> + app:checked="@{state.isNotificationsTestEnabled}" + app:subtitle="@{state.getTestNotificationStatusText(context)}" + app:switchEnabled="@{true}" + app:title="@{@string/settings_notifications_subtitle_update_test}" /> + + <View + android:id="@+id/divider2" + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:background="@color/colorHairline" + app:layout_constraintTop_toBottomOf="@id/settings_switch_row_notifications_test" /> <include android:id="@+id/settings_notifications_card" @@ -125,4 +137,4 @@ </androidx.constraintlayout.widget.ConstraintLayout> -</layout> \ No newline at end of file +</layout> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings_privacy_preserving_analytics.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings_privacy_preserving_analytics.xml index b158a7869eea4a58699eae92cbd43eb8e416905d..eec2f250912951aac825936469576328b904fe19 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_settings_privacy_preserving_analytics.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings_privacy_preserving_analytics.xml @@ -10,25 +10,22 @@ android:fillViewport="true" android:focusable="true"> - <include + <com.google.android.material.appbar.MaterialToolbar android:id="@+id/settings_ppa_header" - layout="@layout/include_header" - android:layout_width="0dp" + style="@style/CWAToolbar.BackArrow" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="@string/settings_background_priority_title" - android:focusable="true" - app:icon="@{@drawable/ic_back}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:title="@{@string/settings_privacy_preserving_analytics_title}" /> + app:title="@string/settings_privacy_preserving_analytics_title" /> <ScrollView android:id="@+id/scrollview" android:layout_width="0dp" android:layout_height="0dp" - android:paddingBottom="@dimen/spacing_normal" android:clipToPadding="false" + android:paddingBottom="@dimen/spacing_normal" android:scrollbarStyle="outsideOverlay" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -64,161 +61,63 @@ android:text="@string/onboarding_ppa_body" app:layout_constraintEnd_toEndOf="@id/body_end" app:layout_constraintStart_toStartOf="@id/body_start" - app:layout_constraintTop_toBottomOf="@+id/onboarding_illustration" /> + app:layout_constraintTop_toBottomOf="@id/onboarding_illustration" /> - <include - android:id="@+id/settings_ppa_switch_row" - layout="@layout/include_settings_switch_row" + <de.rki.coronawarnapp.ui.view.MoreInformationView + android:id="@id/settings_ppa_switch_row" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" android:layout_marginTop="@dimen/spacing_small" - gone="@{false}" - app:enabled="@{true}" + android:focusable="true" + app:isToggleVisible="true" + app:isTopDividerVisible="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/onboarding_body" - app:showDivider="@{true}" - app:status="@{false}" - app:statusText="@{@string/settings_on}" - app:subtitle="@{@string/settings_analytics_switch_subtitle}" /> + app:subtitleText="@string/onboarding_ppa_state_title" + app:titleText="@string/settings_analytics_switch_subtitle" + app:toggleOffText="@string/settings_off" + app:toggleOnText="@string/settings_on" /> - <androidx.constraintlayout.widget.ConstraintLayout + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@id/federal_state_row" - style="@style/row" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:focusable="true" + app:isTopDividerVisible="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/settings_ppa_switch_row"> - - <TextView - android:id="@id/federal_state_row_subtitle" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/onboarding_ppa_state_title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@+id/federal_state_row_body" - style="@style/body2Medium" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_mega_tiny" - android:accessibilityLiveRegion="assertive" - android:focusable="true" - android:text="@string/onboarding_ppa_state_title" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/federal_state_row_subtitle" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - layout="@layout/include_divider" - android:layout_width="0dp" - android:layout_height="@dimen/card_divider" - android:layout_marginStart="@dimen/guideline_start" - android:layout_marginEnd="@dimen/guideline_end" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/federal_state_row" /> + app:layout_constraintTop_toBottomOf="@+id/settings_ppa_switch_row" + app:subtitleText="@string/onboarding_ppa_state_title" + app:titleText="@string/onboarding_ppa_state_title" /> - <androidx.constraintlayout.widget.ConstraintLayout + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@id/district_row" - style="@style/row" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:focusable="true" + app:isTopDividerVisible="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/federal_state_row"> - - <TextView - android:id="@id/district_row_subtitle" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/onboarding_ppa_district_title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@id/district_row_body" - style="@style/body2Medium" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_mega_tiny" - android:accessibilityLiveRegion="assertive" - android:focusable="true" - android:text="@string/onboarding_ppa_district_title" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/district_row_subtitle" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - layout="@layout/include_divider" - android:layout_width="0dp" - android:layout_height="@dimen/card_divider" - android:layout_marginStart="@dimen/guideline_start" - android:layout_marginEnd="@dimen/guideline_end" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/district_row" /> + app:layout_constraintTop_toBottomOf="@id/federal_state_row" + app:subtitleText="@string/onboarding_ppa_district_title" + app:titleText="@string/onboarding_ppa_district_title" /> - <androidx.constraintlayout.widget.ConstraintLayout + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@id/age_group_row" - style="@style/row" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:focusable="true" + app:isTopDividerVisible="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/district_row"> - - <TextView - android:id="@id/age_group_row_subtitle" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/onboarding_ppa_age_title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@id/age_group_row_body" - style="@style/body2Medium" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_mega_tiny" - android:accessibilityLiveRegion="assertive" - android:focusable="true" - android:text="@string/onboarding_ppa_age_title" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/age_group_row_subtitle" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - layout="@layout/include_divider" - android:layout_width="0dp" - android:layout_height="@dimen/card_divider" - android:layout_marginStart="@dimen/guideline_start" - android:layout_marginEnd="@dimen/guideline_end" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/age_group_row" /> + app:layout_constraintTop_toBottomOf="@+id/district_row" + app:subtitleText="@string/onboarding_ppa_age_title" + app:titleText="@string/onboarding_ppa_age_title" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@id/consent_layout" @@ -227,9 +126,9 @@ android:layout_height="wrap_content" android:layout_marginTop="32dp" android:orientation="vertical" - app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" - app:layout_constraintStart_toStartOf="@+id/guideline_card_start" - app:layout_constraintTop_toBottomOf="@+id/age_group_row"> + app:layout_constraintEnd_toStartOf="@id/guideline_card_end" + app:layout_constraintStart_toStartOf="@id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/age_group_row"> <TextView android:id="@id/legal_title" @@ -275,9 +174,9 @@ android:layout_height="@dimen/bullet_point_size" android:layout_marginTop="@dimen/spacing_tiny" android:importantForAccessibility="no" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/legal_point_consent" /> + app:layout_constraintTop_toTopOf="@id/legal_point_consent" + app:srcCompat="@drawable/bullet_point" /> <TextView android:id="@id/legal_point_identity" @@ -298,9 +197,9 @@ android:layout_height="@dimen/bullet_point_size" android:layout_marginTop="@dimen/spacing_tiny" android:importantForAccessibility="no" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/legal_point_identity" /> + app:layout_constraintTop_toTopOf="@id/legal_point_identity" + app:srcCompat="@drawable/bullet_point" /> <TextView android:id="@id/legal_point_sixteen" @@ -313,7 +212,7 @@ android:text="@string/ppa_onboarding_privacy_information_point_sixteen" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/bullet_point_consent" - app:layout_constraintTop_toBottomOf="@+id/legal_point_identity" /> + app:layout_constraintTop_toBottomOf="@id/legal_point_identity" /> <androidx.appcompat.widget.AppCompatImageView android:id="@id/bullet_point_sixteen" @@ -321,44 +220,23 @@ android:layout_height="@dimen/bullet_point_size" android:layout_marginTop="@dimen/spacing_tiny" android:importantForAccessibility="no" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/legal_point_sixteen" /> + app:layout_constraintTop_toTopOf="@id/legal_point_sixteen" + app:srcCompat="@drawable/bullet_point" /> </androidx.constraintlayout.widget.ConstraintLayout> - <androidx.constraintlayout.widget.ConstraintLayout + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@id/more_info_row" - style="@style/row" - android:layout_marginTop="@dimen/spacing_normal" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="16dp" + android:focusable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/consent_layout"> - - <TextView - android:id="@id/more_info_title" - style="@style/body1" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/onboarding_ppa_more_info_title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - layout="@layout/include_divider" - android:layout_width="0dp" - android:layout_height="@dimen/card_divider" - android:layout_marginStart="@dimen/guideline_start" - android:layout_marginEnd="@dimen/guideline_end" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/more_info_row" /> + app:layout_constraintTop_toBottomOf="@id/consent_layout" + app:titleText="@string/onboarding_ppa_more_info_title" /> <androidx.constraintlayout.widget.Guideline android:id="@id/body_start" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings_tracing.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings_tracing.xml index 95ba2919e5222e310134520cb6fb1d7e250f2d40..906b8f3dd1a67de9385be0f5034837247e0beaab 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_settings_tracing.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings_tracing.xml @@ -77,39 +77,44 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/illustration" /> - <include - android:id="@+id/settings_tracing_switch_row" - layout="@layout/include_settings_switch_row" + <de.rki.coronawarnapp.ui.view.SwitchRowView + android:id="@+id/switch_row" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - app:enabled="@{settingsTracingState.isTracingSwitchEnabled()}" + app:checked="@{settingsTracingState.isTracingSwitchChecked()}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/information_details_header_headline" - app:showDivider="@{true}" - app:status="@{settingsTracingState.isTracingSwitchChecked()}" - app:statusText="@{settingsTracingState.getTracingStatusText(context)}" - app:subtitle="@{@string/settings_tracing_title}" /> + app:subtitle="@{settingsTracingState.getTracingStatusText(context)}" + app:switchEnabled="@{settingsTracingState.isTracingSwitchEnabled()}" + app:title="@{@string/settings_tracing_title}" /> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:background="@color/colorHairline" + app:layout_constraintTop_toBottomOf="@+id/switch_row" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/settingsInteroperabilityRow" android:layout_width="0dp" android:layout_height="wrap_content" + android:background="?android:selectableItemBackground" android:paddingTop="@dimen/spacing_tiny" android:paddingBottom="@dimen/spacing_tiny" - android:background="?android:selectableItemBackground" - app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintEnd_toStartOf="@id/guideline_end" - app:layout_constraintTop_toBottomOf="@id/settings_tracing_switch_row"> + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/switch_row"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/flag_eu" android:layout_width="26dp" android:layout_height="26dp" - app:srcCompat="@drawable/ic_country_eu" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_country_eu" /> <TextView android:id="@+id/interoperability_title" @@ -126,9 +131,9 @@ android:id="@+id/flag_ch" android:layout_width="0dp" android:layout_height="22dp" - app:srcCompat="@drawable/ic_country_ch" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/interoperability_title" /> + app:layout_constraintTop_toBottomOf="@id/interoperability_title" + app:srcCompat="@drawable/ic_country_ch" /> <TextView android:id="@+id/interoperability_subtitle" @@ -151,96 +156,85 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/settingsInteroperabilityRow" /> - <androidx.constraintlayout.widget.ConstraintLayout + + <androidx.constraintlayout.helper.widget.Flow android:id="@+id/settings_tracing_status" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_small" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/settingsInteroperabilityRow"> - - <include - android:id="@+id/settings_tracing_status_location" - gone="@{!settingsTracingState.isLocationCardVisible()}" - layout="@layout/include_tracing_status_card_location" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:buttonText="@{@string/settings_tracing_status_location_button}" - app:headline="@{@string/settings_tracing_status_location_headline}" - app:icon="@{@drawable/ic_location}" - app:layout_constraintEnd_toStartOf="@id/guideline_card_end" - app:layout_constraintStart_toStartOf="@id/guideline_card_start" - app:layout_constraintTop_toTopOf="parent" /> - - <include - android:id="@+id/settings_tracing_status_bluetooth" - gone="@{!settingsTracingState.isBluetoothCardVisible()}" - layout="@layout/include_tracing_status_card" - android:layout_marginBottom="@dimen/spacing_small" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:body="@{@string/settings_tracing_status_bluetooth_body}" - app:buttonText="@{@string/settings_tracing_status_bluetooth_button}" - app:headline="@{@string/settings_tracing_status_bluetooth_headline}" - app:icon="@{@drawable/ic_settings_tracing_bluetooth}" - app:layout_constraintEnd_toStartOf="@id/guideline_card_end" - app:layout_constraintStart_toStartOf="@id/guideline_card_start" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@+id/risk_details_period_logged_headline" - style="@style/headline5" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="false" - android:layout_marginTop="@dimen/spacing_small" - android:text="@string/risk_details_headline_period_logged" - app:layout_constraintEnd_toStartOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/settings_tracing_status_bluetooth" /> + android:orientation="horizontal" + app:constraint_referenced_ids="settings_tracing_status_location,settings_tracing_status_bluetooth,risk_details_period_logged_headline,risk_details_period_logged_subtitle,risk_details_period_logged_body_notice,risk_details_period_logged_days" + app:flow_maxElementsWrap="1" + app:flow_verticalBias="0" + app:flow_verticalStyle="packed" + app:flow_wrapMode="chain" + app:layout_constraintEnd_toEndOf="@id/guideline_end" + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/settingsInteroperabilityRow" /> - <TextView - android:id="@+id/risk_details_period_logged_subtitle" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_tiny" - android:focusable="false" - android:text="@string/risk_details_subtitle_period_logged" - app:layout_constraintEnd_toStartOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/risk_details_period_logged_headline" /> + <include + android:id="@+id/settings_tracing_status_location" + gone="@{!settingsTracingState.isLocationCardVisible()}" + layout="@layout/include_tracing_status_card_location" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:buttonText="@{@string/settings_tracing_status_location_button}" + app:headline="@{@string/settings_tracing_status_location_headline}" + app:icon="@{@drawable/ic_location}" /> - <TextView - android:id="@id/risk_details_period_logged_body_notice" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - android:focusable="true" - android:text="@string/risk_details_information_body_period_logged" - app:layout_constraintEnd_toStartOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/risk_details_period_logged_subtitle" /> + <include + android:id="@+id/settings_tracing_status_bluetooth" + gone="@{!settingsTracingState.isBluetoothCardVisible()}" + layout="@layout/include_tracing_status_card" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:body="@{@string/settings_tracing_status_bluetooth_body}" + app:buttonText="@{@string/settings_tracing_status_bluetooth_button}" + app:headline="@{@string/settings_tracing_status_bluetooth_headline}" + app:icon="@{@drawable/ic_settings_tracing_bluetooth}" /> - <TextView - android:id="@+id/risk_details_period_logged_days" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - android:focusable="true" - android:text="@{loggedPeriod.getInstallTimePeriodLogged(context)}" - app:layout_constraintEnd_toStartOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/risk_details_period_logged_body_notice" /> + <TextView + android:id="@+id/risk_details_period_logged_headline" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:focusable="false" + android:paddingTop="@dimen/spacing_small" + android:text="@string/risk_details_headline_period_logged" + tools:ignore="MissingConstraints" /> - <include layout="@layout/merge_guidelines_card" /> + <TextView + android:id="@+id/risk_details_period_logged_subtitle" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:focusable="false" + android:paddingTop="@dimen/spacing_tiny" + android:text="@string/risk_details_subtitle_period_logged" + tools:ignore="MissingConstraints" /> - <include layout="@layout/merge_guidelines_side" /> + <TextView + android:id="@id/risk_details_period_logged_body_notice" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:focusable="true" + android:paddingTop="@dimen/spacing_normal" + android:text="@string/risk_details_information_body_period_logged" + tools:ignore="MissingConstraints" /> - </androidx.constraintlayout.widget.ConstraintLayout> + <TextView + android:id="@+id/risk_details_period_logged_days" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:focusable="true" + android:paddingTop="@dimen/spacing_normal" + android:text="@{loggedPeriod.getInstallTimePeriodLogged(context)}" + tools:ignore="MissingConstraints" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/risk_details_body" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_no_consent_positive_other_warning.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_no_consent_positive_other_warning.xml index 080cab6b67d87c6305648406bb8aaad6cffd7a75..07ef45ea131f55b072dff90511434bd3a21dc6a9 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_no_consent_positive_other_warning.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_no_consent_positive_other_warning.xml @@ -77,19 +77,6 @@ app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@id/submission_positive_other_warning_headline" /> - <TextView - android:id="@+id/submission_positive_other_warning_text_second_part" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - android:focusable="true" - android:text="@string/submission_positive_other_warning_no_consent_body_second_part" - app:layout_constraintEnd_toEndOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/submission_positive_other_warning_text_first_part" /> - - <de.rki.coronawarnapp.ui.view.CountryListView android:id="@+id/countryList" android:layout_width="0dp" @@ -97,7 +84,7 @@ android:layout_marginTop="@dimen/spacing_normal" app:layout_constraintEnd_toEndOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/submission_positive_other_warning_text_second_part" /> + app:layout_constraintTop_toBottomOf="@id/submission_positive_other_warning_text_first_part" /> <include android:id="@+id/submission_positive_other_privacy" @@ -115,7 +102,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - app:itemText="@{@string/submission_consent_main_first_point}" + app:itemText="@{@string/submission_no_consent_first_point}" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@id/submission_positive_other_privacy" /> @@ -126,7 +113,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_tiny" - app:itemText="@{@string/submission_consent_main_third_point}" + app:itemText="@{@string/submission_no_consent_second_point}" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_first_point" /> @@ -137,11 +124,33 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_tiny" - app:itemText="@{@string/submission_consent_main_fourth_point}" + app:itemText="@{@string/submission_no_consent_third_point}" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_second_point" /> + <include + android:id="@+id/submission_no_consent_main_fourth_point" + layout="@layout/view_bullet_point_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_tiny" + app:itemText="@{@string/submission_no_consent_fourth_point}" + app:layout_constraintEnd_toEndOf="@id/guideline_end" + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_third_point" /> + + <include + android:id="@+id/submission_no_consent_main_fifth_point" + layout="@layout/view_bullet_point_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_tiny" + app:itemText="@{@string/submission_no_consent_fifth_point}" + app:layout_constraintEnd_toEndOf="@id/guideline_end" + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_fourth_point" /> + <FrameLayout android:id="@+id/divider" android:layout_width="0dp" @@ -150,7 +159,7 @@ android:background="@color/colorHairline" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_third_point" /> + app:layout_constraintTop_toBottomOf="@id/submission_no_consent_main_fifth_point" /> <TextView android:id="@+id/submission_consent_main_bottom_body" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_your_consent.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_your_consent.xml index 6afea3003ca30db6bce0e8f2d62bbe96c696d352..20ee30c8264574e6cb9bedc72c0e2314a4026fb9 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_your_consent.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_your_consent.xml @@ -37,18 +37,24 @@ android:layout_height="wrap_content" android:focusable="true"> - <include + <de.rki.coronawarnapp.ui.view.SwitchRowView android:id="@+id/submission_your_consent_switch" - layout="@layout/include_settings_switch_row" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_small" - app:enabled="@{true}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:showDivider="@{true}" - app:subtitle="@{@string/submission_your_consent_switch_subtitle}" /> + app:switchEnabled="@{true}" + app:title="@{@string/submission_your_consent_switch_subtitle}" /> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:background="@color/colorHairline" + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_switch" /> <TextView android:id="@+id/submission_your_consent_about_text" @@ -59,7 +65,7 @@ android:text="@string/submission_your_consent_about_agreement" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@+id/submission_your_consent_switch" /> + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_switch" /> <LinearLayout android:id="@+id/submission_your_consent_agreement_card" @@ -69,9 +75,9 @@ android:layout_marginTop="@dimen/spacing_medium" android:focusable="true" android:orientation="vertical" - app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" - app:layout_constraintStart_toStartOf="@+id/guideline_card_start" - app:layout_constraintTop_toBottomOf="@+id/submission_your_consent_about_text"> + app:layout_constraintEnd_toStartOf="@id/guideline_card_end" + app:layout_constraintStart_toStartOf="@id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_about_text"> <TextView android:id="@+id/submission_your_consent_agreement_title" @@ -81,7 +87,7 @@ android:layout_marginEnd="@dimen/spacing_small" android:accessibilityHeading="true" android:contentDescription="@string/submission_your_consent_agreement_title" - android:text="@string/submission_your_consent_agreement_title"/> + android:text="@string/submission_your_consent_agreement_title" /> <TextView android:id="@+id/submission_your_consent_agreement_share_test_results_text" @@ -114,8 +120,8 @@ android:layout_marginTop="@dimen/spacing_medium" android:background="@color/colorHairline" app:layout_constraintEnd_toEndOf="@+id/guideline_end" - app:layout_constraintStart_toStartOf="@+id/guideline_start" - app:layout_constraintTop_toBottomOf="@+id/submission_your_consent_agreement_card" /> + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_agreement_card" /> <TextView android:id="@+id/submission_your_consent_agreement_details_text" @@ -128,7 +134,7 @@ android:text="@string/submission_your_consent_agreement_details" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@+id/submission_your_consent_agreement_details_divider_top" /> + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_agreement_details_divider_top" /> <View android:id="@+id/submission_your_consent_agreement_details_divider_bottom" @@ -136,8 +142,8 @@ android:layout_height="@dimen/card_divider" android:background="@color/colorHairline" app:layout_constraintEnd_toEndOf="@+id/guideline_end" - app:layout_constraintStart_toStartOf="@+id/guideline_start" - app:layout_constraintTop_toBottomOf="@+id/submission_your_consent_agreement_details_text" /> + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_your_consent_agreement_details_text" /> <include layout="@layout/merge_guidelines_side" /> <include layout="@layout/merge_guidelines_card" /> @@ -157,4 +163,4 @@ </androidx.constraintlayout.widget.ConstraintLayout> -</layout> \ No newline at end of file +</layout> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_trace_location_onboarding.xml b/Corona-Warn-App/src/main/res/layout/fragment_trace_location_onboarding.xml index d86a5a7de4b057adcb9d0cd8756c793e75343d38..d353db870dab7c855bbe9eefab89fc3309a3cc2c 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_trace_location_onboarding.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_trace_location_onboarding.xml @@ -23,17 +23,17 @@ android:layout_marginHorizontal="@dimen/spacing_normal" android:layout_marginVertical="@dimen/spacing_small" android:text="@string/trace_location_onboarding_body_confirm" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_scroll_view" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_scroll_view" /> - <androidx.core.widget.NestedScrollView + <ScrollView android:id="@+id/check_in_onboarding_scroll_view" android:layout_width="match_parent" android:layout_height="0dp" - android:paddingBottom="@dimen/spacing_small" android:clipToPadding="false" + android:paddingBottom="@dimen/spacing_small" android:scrollbarStyle="outsideOverlay" app:layout_constraintBottom_toTopOf="@+id/check_in_onboarding_acknowledge" app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_toolbar"> @@ -49,10 +49,10 @@ android:layout_marginHorizontal="24dp" android:layout_marginTop="4dp" android:contentDescription="@string/trace_location_onboarding_content_description" - app:srcCompat="@drawable/trace_location_onboarding" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/trace_location_onboarding" /> <TextView android:id="@+id/check_in_onboarding_title" @@ -86,9 +86,9 @@ android:layout_marginStart="@dimen/spacing_normal" android:layout_marginTop="@dimen/spacing_large" android:importantForAccessibility="no" - app:srcCompat="@drawable/ic_qr_tracing_static" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_subtitle" /> + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_subtitle" + app:srcCompat="@drawable/ic_qr_tracing_static" /> <TextView android:id="@+id/check_in_onboarding_warning" @@ -108,9 +108,9 @@ android:layout_marginStart="@dimen/spacing_normal" android:layout_marginTop="@dimen/spacing_medium" android:importantForAccessibility="no" - app:srcCompat="@drawable/ic_qr_time" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_warning" /> + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_warning" + app:srcCompat="@drawable/ic_qr_time" /> <TextView android:id="@+id/check_in_onboarding_stay" @@ -128,10 +128,8 @@ style="@style/cardTracing" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="24dp" + android:layout_marginHorizontal="@dimen/guideline_card" android:layout_marginTop="24dp" - android:background="#F5F5F5" - android:orientation="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_stay"> @@ -161,9 +159,9 @@ android:layout_width="8dp" android:layout_height="8dp" android:layout_marginTop="24dp" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_card_subtitle" /> + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_card_subtitle" + app:srcCompat="@drawable/bullet_point" /> <TextView android:id="@+id/check_in_onboarding_body2" @@ -197,9 +195,9 @@ android:layout_width="8dp" android:layout_height="8dp" android:layout_marginTop="22dp" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_body3" /> + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_body3" + app:srcCompat="@drawable/bullet_point" /> <TextView android:id="@+id/check_in_onboarding_body4" @@ -220,9 +218,9 @@ android:layout_width="8dp" android:layout_height="8dp" android:layout_marginTop="22dp" - app:srcCompat="@drawable/bullet_point" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_body4" /> + app:layout_constraintTop_toBottomOf="@id/check_in_onboarding_body4" + app:srcCompat="@drawable/bullet_point" /> <TextView android:id="@+id/check_in_onboarding_body5" @@ -252,7 +250,7 @@ </androidx.constraintlayout.widget.ConstraintLayout> - <de.rki.coronawarnapp.util.ui.views.MoreInformationView + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@+id/check_in_onboarding_privacy" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -262,6 +260,6 @@ app:layout_constraintTop_toBottomOf="@+id/check_in_onboarding_card_container" app:titleText="@string/contact_diary_onboarding_legal_information" /> </androidx.constraintlayout.widget.ConstraintLayout> - </androidx.core.widget.NestedScrollView> + </ScrollView> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/home_fragment_layout.xml b/Corona-Warn-App/src/main/res/layout/home_fragment_layout.xml index 683de0e8d2f457d3cfe850a568d1d6439032a072..79b703795f468257a51a13d58ebfecdfef24be0e 100644 --- a/Corona-Warn-App/src/main/res/layout/home_fragment_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_fragment_layout.xml @@ -18,7 +18,7 @@ android:orientation="vertical" tools:context=".ui.main.MainActivity"> - <Toolbar + <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -34,7 +34,7 @@ app:srcCompat="@drawable/ic_main_header" bind:cwaContentDescription="@{@string/accessibility_logo}" tools:ignore="ContentDescription" /> - </Toolbar> + </com.google.android.material.appbar.MaterialToolbar> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" diff --git a/Corona-Warn-App/src/main/res/layout/include_privacy_card_no_consent.xml b/Corona-Warn-App/src/main/res/layout/include_privacy_card_no_consent.xml index 7fc07916734d6ca6c76ced7e8e78bd562b21bded..132c5412a7f4acffba11926d3da22edc423ca5a1 100644 --- a/Corona-Warn-App/src/main/res/layout/include_privacy_card_no_consent.xml +++ b/Corona-Warn-App/src/main/res/layout/include_privacy_card_no_consent.xml @@ -58,6 +58,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/privacy_card_text_part_second" /> + <TextView + android:id="@+id/privacy_card_text_part_fourth" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_normal" + android:text="@string/submission_no_consent_your_consent_subsection_body_fourth_part" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/privacy_card_text_part_third" /> + </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/include_settings_switch_row.xml b/Corona-Warn-App/src/main/res/layout/include_settings_switch_row.xml deleted file mode 100644 index ccab31e83d96b4de4c1f9e434da38e717af6b484..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/res/layout/include_settings_switch_row.xml +++ /dev/null @@ -1,118 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<layout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> - - <data> - <variable - name="subtitle" - type="String" /> - - <variable - name="enabled" - type="Boolean" /> - - <variable - name="status" - type="Boolean" /> - - <variable - name="statusText" - type="String" /> - - <variable - name="showDivider" - type="Boolean" /> - - </data> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/settings_switch_row" - style="@style/row" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:focusable="true" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/settings_switch_row_header" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/spacing_normal" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/settings_switch_row_switch" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <TextView - android:id="@+id/settings_switch_row_header_subtitle" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:text="@{subtitle}" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@+id/settings_switch_row_header_body" - style="@style/body2Medium" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_mega_tiny" - android:accessibilityLiveRegion="assertive" - android:text="@{statusText}" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/settings_switch_row_header_subtitle" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <com.google.android.material.switchmaterial.SwitchMaterial - android:id="@+id/settings_switch_row_switch" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:checked="@{status}" - android:importantForAccessibility="no" - android:enabled="@{enabled}" - android:contentDescription="@{subtitle + ` ` + status}" - android:theme="@style/switchBase" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - android:id="@+id/divider" - layout="@layout/include_divider" - android:layout_width="0dp" - android:layout_height="@dimen/card_divider" - gone="@{!showDivider}" - app:layout_constraintEnd_toEndOf="@id/guideline_end" - app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@+id/settings_switch_row" /> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/guideline_start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintGuide_begin="@dimen/guideline_start" /> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/guideline_end" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintGuide_end="@dimen/guideline_end" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - -</layout> diff --git a/Corona-Warn-App/src/main/res/layout/rat_profile_onboarding_fragment.xml b/Corona-Warn-App/src/main/res/layout/rat_profile_onboarding_fragment.xml index 33891c8dc9f9a4ca984f2dd214336c686a35eb0d..e8edce2e075fa3a689e3697f6d15eaee982e5950 100644 --- a/Corona-Warn-App/src/main/res/layout/rat_profile_onboarding_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/rat_profile_onboarding_fragment.xml @@ -1,45 +1,273 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.coronatest.rat.profile.onboarding.RATProfileOnboardingFragment"> + android:background="@color/colorBackground"> - <!-- TODO: implement onboarding screen--> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" style="@style/CWAToolbar.Close" android:layout_width="0dp" - android:layout_height="wrap_content" + android:layout_height="?actionBarSize" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:title="Schnelltest-Profil" /> + app:title="@string/rat_profile_create_title" /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:text="🚧 Onboarding Screen" - android:textSize="40sp" - app:layout_constraintBottom_toTopOf="@+id/nextButton" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + <ScrollView + android:id="@+id/rat_profile_onboarding_scroll_view" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_marginBottom="20dp" + app:layout_constraintBottom_toTopOf="@id/next_button" + app:layout_constraintTop_toBottomOf="@id/toolbar"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/rat_profile_onboarding_image" + android:layout_width="0dp" + android:layout_height="190dp" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="4dp" + android:contentDescription="@string/rat_profile_onboarding_image_content_description" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/rat_profile_onboarding" /> + + <TextView + android:id="@+id/rat_profile_onboarding_title" + style="@style/headline6" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="24dp" + android:text="@string/rat_profile_onboarding_title" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_image" /> + + <TextView + android:id="@+id/rat_profile_onboarding_subtitle" + style="@style/subtitleMedium" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="22dp" + android:text="@string/rat_profile_onboarding_subtitle" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_title" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/rat_profile_onboarding_card_container" + style="@style/cardTracing" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/guideline_card" + android:layout_marginTop="32dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_subtitle"> + + <TextView + android:id="@+id/rat_profile_onboarding_card_title" + style="@style/headline6" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/rat_profile_onboarding_card_title" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point1" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_card_title" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body1" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point1" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_card_title" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point2" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body1" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body2" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point2" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body1" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point3" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body2" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body3" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent3" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point3" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body2" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point4" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body3" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body4" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent4" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point4" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body3" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point5" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body4" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body5" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent5" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point5" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body4" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point6" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body5" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body6" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent6" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point6" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body5" /> + + <ImageView + android:id="@+id/rat_profile_onboarding_bullet_point7" + android:layout_width="8dp" + android:layout_height="8dp" + android:layout_marginTop="22dp" + android:importantForAccessibility="no" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body6" + app:srcCompat="@drawable/bullet_point" /> + + <TextView + android:id="@+id/rat_profile_onboarding_body7" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="16dp" + android:focusable="true" + android:text="@string/rat_profile_onboarding_card_consent7" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/rat_profile_onboarding_bullet_point7" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_body6" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + <de.rki.coronawarnapp.ui.view.MoreInformationView + android:id="@+id/rat_profile_onboarding_privacy" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="30dp" + android:focusable="true" + app:layout_constraintTop_toBottomOf="@id/rat_profile_onboarding_card_container" + app:titleText="@string/rat_profile_onboarding_privacy" /> + </androidx.constraintlayout.widget.ConstraintLayout> + </ScrollView> <Button - android:id="@+id/nextButton" + android:id="@+id/next_button" style="@style/buttonPrimary" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="bottom|center" android:layout_marginHorizontal="24dp" android:layout_marginBottom="24dp" - android:text="Weiter" + android:text="@string/rat_profile_onboarding_next" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> -</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/rat_profile_qr_code_fragment.xml b/Corona-Warn-App/src/main/res/layout/rat_profile_qr_code_fragment.xml index b1467f8500be0855d3ac060e51f2163500da632c..3c990f83bc78fdc54fd39988a01e1aff7c8adcd6 100644 --- a/Corona-Warn-App/src/main/res/layout/rat_profile_qr_code_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/rat_profile_qr_code_fragment.xml @@ -1,26 +1,198 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/content_container" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragment"> + android:background="@drawable/trace_location_gradient_background" + android:contentDescription="@string/trace_location_event_detail_title_accessibility"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:text="🚧 Profile Screen" - android:textSize="40sp" /> + <androidx.coordinatorlayout.widget.CoordinatorLayout + android:id="@+id/coordinator_layout" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginBottom="12dp" + android:nestedScrollingEnabled="true" + app:layout_constraintBottom_toTopOf="@id/next_button" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:context=".CollapsingToolbar"> + + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/appBarLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.google.android.material.appbar.CollapsingToolbarLayout + android:id="@+id/collapsing_toolbar_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:nestedScrollingEnabled="true" + app:layout_scrollFlags="scroll|exitUntilCollapsed"> + + <ImageView + android:id="@+id/expandedImage" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_collapseMode="parallax" + app:srcCompat="@drawable/rat_profile_gradient" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_collapseMode="parallax"> + + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="90dp" + android:layout_marginBottom="12dp" + android:gravity="center" + android:text="@string/rat_profile_create_title" + android:textColor="@android:color/white" + android:textSize="20sp" + android:textStyle="bold" /> + + </LinearLayout> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:theme="@style/CWAToolbar.Theme" + app:layout_collapseMode="pin" + app:layout_scrollFlags="scroll|enterAlways" + app:menu="@menu/menu_rat_qr_code_profile" + app:navigationIcon="@drawable/ic_close" + app:navigationIconTint="@android:color/white"> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="24dp"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:importantForAccessibility="no" + android:scaleType="center" + app:srcCompat="@drawable/ic_cwa_logo_white" /> + </FrameLayout> + + </com.google.android.material.appbar.MaterialToolbar> + + </com.google.android.material.appbar.CollapsingToolbarLayout> + + </com.google.android.material.appbar.AppBarLayout> + + <androidx.core.widget.NestedScrollView + android:id="@+id/nested_scroll_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:animateLayoutChanges="true"> + + <ImageView + android:id="@+id/qrCodeImage" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginHorizontal="24dp" + android:layout_marginVertical="12dp" + android:layout_marginTop="16dp" + android:background="@drawable/rounded_white_background" + android:contentDescription="@string/trace_location_event_detail_qr_code_accessibility" + app:layout_constraintDimensionRatio="H,1:1" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:src="@drawable/ic_qrcode" + tools:tint="@android:color/black" /> + + <com.google.android.material.progressindicator.LinearProgressIndicator + android:id="@+id/progress_bar" + android:layout_width="150dp" + android:layout_height="wrap_content" + android:indeterminate="true" + app:hideAnimationBehavior="inward" + app:indicatorColor="@color/colorAccent" + app:layout_constraintBottom_toBottomOf="@id/qrCodeImage" + app:layout_constraintEnd_toEndOf="@id/qrCodeImage" + app:layout_constraintStart_toStartOf="@id/qrCodeImage" + app:layout_constraintTop_toTopOf="@id/qrCodeImage" /> + + <LinearLayout + android:id="@+id/info_box" + style="@style/Card.NoElevation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="8dp" + android:orientation="vertical" + android:padding="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/qrCodeImage" + app:layout_constraintVertical_chainStyle="packed"> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="8dp" + android:text="@string/rat_qr_code_profile_description" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/person_data" + style="@style/Card.NoElevation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="8dp" + android:orientation="vertical" + android:padding="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/info_box" + app:layout_constraintVertical_chainStyle="packed"> + + <TextView + android:id="@+id/profile_info" + style="@style/materialSubtitleSixteen" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:lineSpacingExtra="8dp" + tools:text="Max Mustermann\ngeboren 17.11.1990\nLange Straße 51\n4471 Potsdam\n0151123456789\nmaxmustermann@web.de" /> + + </LinearLayout> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </androidx.core.widget.NestedScrollView> + </androidx.coordinatorlayout.widget.CoordinatorLayout> <Button - android:id="@+id/closeButton" + android:id="@+id/next_button" style="@style/buttonPrimary" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_gravity="bottom|center" - android:layout_marginHorizontal="24dp" - android:layout_marginBottom="24dp" - android:text="Weiter" /> + android:layout_marginStart="@dimen/spacing_normal" + android:layout_marginEnd="@dimen/spacing_normal" + android:layout_marginBottom="8dp" + android:text="@string/rat_qr_code_profile_next_button" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> -</FrameLayout> \ No newline at end of file +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/switch_row.xml b/Corona-Warn-App/src/main/res/layout/switch_row.xml new file mode 100644 index 0000000000000000000000000000000000000000..502698f17d03759a11dc7ff7ac88bfeea12bcd5a --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/switch_row.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> + +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/switch_row" + style="@style/row" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <TextView + android:id="@+id/switch_title" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/spacing_normal" + app:layout_constraintEnd_toStartOf="@+id/switch_view" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="Setting" /> + + <TextView + android:id="@+id/switch_subtitle" + style="@style/body2Medium" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_mega_tiny" + android:layout_marginEnd="@dimen/spacing_normal" + android:accessibilityLiveRegion="assertive" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/switch_view" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/switch_title" + tools:text="Active" /> + + <com.google.android.material.switchmaterial.SwitchMaterial + android:id="@+id/switch_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="false" + android:importantForAccessibility="no" + android:theme="@style/switchBase" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_detail_fragment.xml b/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_detail_fragment.xml index 36589681fc501f9e38582a7790969b2071e1dab8..8ffc939f6e8957cdb5d1c5923692cc43418eb0f0 100644 --- a/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_detail_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_detail_fragment.xml @@ -122,7 +122,7 @@ android:layout_marginHorizontal="24dp" android:layout_marginVertical="12dp" android:layout_marginTop="16dp" - android:background="@drawable/trace_location_qr_code_background" + android:background="@drawable/rounded_white_background" android:contentDescription="@string/trace_location_event_detail_qr_code_accessibility" app:layout_constraintDimensionRatio="H,1:1" app:layout_constraintLeft_toLeftOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_info_fragment.xml b/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_info_fragment.xml index de5948e1fe8de67d48496593342c7b4bda2dde40..c7326c58b36f99479702aad56d81a6d1149cd79e 100644 --- a/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_info_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/trace_location_organizer_qr_code_info_fragment.xml @@ -148,7 +148,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/trace_location_qr_time_sheet_text" /> - <de.rki.coronawarnapp.util.ui.views.MoreInformationView + <de.rki.coronawarnapp.ui.view.MoreInformationView android:id="@+id/privacy_information" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -182,4 +182,4 @@ app:layout_constraintStart_toStartOf="parent" tools:text="@string/acknowledge_button" /> -</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/view_more_information.xml b/Corona-Warn-App/src/main/res/layout/view_more_information.xml index f5248050c17666091569256a668cbd4c53f03eea..b5d13fa851b1189c26dcf777d4d21dd3c95adfb6 100644 --- a/Corona-Warn-App/src/main/res/layout/view_more_information.xml +++ b/Corona-Warn-App/src/main/res/layout/view_more_information.xml @@ -2,8 +2,8 @@ <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - tools:layout_width="match_parent" tools:layout_height="wrap_content" + tools:layout_width="match_parent" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> <View @@ -16,32 +16,47 @@ app:layout_constraintTop_toTopOf="parent" /> <TextView - android:id="@+id/more_information_title" + android:id="@+id/titleElement" style="@style/subtitle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="16dp" - app:layout_constraintBottom_toTopOf="@id/more_information_subtitle" - app:layout_constraintEnd_toEndOf="parent" + android:layout_marginEnd="8dp" + app:layout_constraintBottom_toTopOf="@id/subtitleElement" + app:layout_constraintEnd_toStartOf="@id/toggle" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_goneMarginBottom="16dp" app:layout_constraintVertical_chainStyle="packed" + app:layout_goneMarginBottom="16dp" tools:text="Custom Title (is mandatory)" /> <TextView - android:id="@+id/more_information_subtitle" - style="@style/TextAppearance.AppCompat.Caption" + android:id="@+id/subtitleElement" + style="@style/body2Medium" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="4dp" + android:layout_marginEnd="8dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@id/toggle" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/more_information_title" + app:layout_constraintTop_toBottomOf="@id/titleElement" tools:text="Custom Subtitle (is optional)" /> + <com.google.android.material.switchmaterial.SwitchMaterial + android:id="@+id/toggle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:checked="false" + android:importantForAccessibility="no" + android:theme="@style/switchBase" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" /> + <View android:id="@+id/bottom_divider" android:layout_width="match_parent" diff --git a/Corona-Warn-App/src/main/res/menu/menu_rat_qr_code_profile.xml b/Corona-Warn-App/src/main/res/menu/menu_rat_qr_code_profile.xml new file mode 100644 index 0000000000000000000000000000000000000000..7ee450aee57a945098e8e29ce8ffaf3a4cfa7612 --- /dev/null +++ b/Corona-Warn-App/src/main/res/menu/menu_rat_qr_code_profile.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <item + android:title="@string/rat_qr_code_profile_delete_item" + app:showAsAction="never" /> +</menu> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/rapid_antigen_test_profile_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/rapid_antigen_test_profile_nav_graph.xml index 19f817795d0d7c177388de3a0bbde2b9a918606b..09d3bd47f25919ae4124271e1b47b91f0eeb93e6 100644 --- a/Corona-Warn-App/src/main/res/navigation/rapid_antigen_test_profile_nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/rapid_antigen_test_profile_nav_graph.xml @@ -12,7 +12,12 @@ tools:layout="@layout/rat_profile_onboarding_fragment"> <action android:id="@+id/action_ratProfileOnboardingFragment_to_ratProfileCreateFragment" - app:destination="@id/ratProfileCreateFragment" /> + app:destination="@id/ratProfileCreateFragment" + app:popUpTo="@id/rapid_test_profile_nav_graph" + app:popUpToInclusive="false" /> + <action + android:id="@+id/action_ratProfileOnboardingFragment_to_privacyFragment" + app:destination="@id/privacyFragment" /> </fragment> <fragment android:id="@+id/ratProfileCreateFragment" @@ -30,4 +35,10 @@ android:name="de.rki.coronawarnapp.ui.coronatest.rat.profile.qrcode.RATProfileQrCodeFragment" android:label="rat_profile_qr_code_fragment" tools:layout="@layout/rat_profile_qr_code_fragment" /> + + <fragment + android:id="@+id/privacyFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationPrivacyFragment" + android:label="InformationPrivacyFragment" + tools:layout="@layout/fragment_information_privacy" /> </navigation> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3144dd73f17dc45a2bc7ac9ff3561008b4b66660 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + + <!-- #################################### + Homescreen cards - register test card + ###################################### --> + <!-- XHED: Register test homescreen card: title --> + <string name="ag_homescreen_card_test_register_title">"РегиÑтрирайте ÑÐ²Ð¾Ñ Ñ‚ÐµÑÑ‚"</string> + <!-- XTXT: Register test homescreen card: body --> + <string name="ag_homescreen_card_test_register_body">"Използвайте приложението, за да извлечете резултатите от теÑта и да предупредите оÑтаналите по-бързо."</string> + + <!-- #################################### + Homescreen cards - common buttons + ###################################### --> + <!-- XBUT: Homescreen card: show test button --> + <string name="ag_homescreen_card_show_button">"Показване на теÑÑ‚"</string> + <!-- XBUT: Homescreen card: warn others button --> + <string name="ag_homescreen_card_warn_others_button">"Предупредете другите"</string> + + <!-- #################################### + Homescreen cards - common status + ###################################### --> + <!-- XTXT: Homescreen card: status subtitle - no result --> + <string name="ag_homescreen_card_status_no_result">"Ð’Ñе още нÑма резултат от теÑта Ви"</string> + <!-- XTXT: Homescreen card: status subtitle - result available --> + <string name="ag_homescreen_card_status_result_available">"Извличане на резултат от теÑÑ‚"</string> + <!-- XTXT: Homescreen card: status subtitle - error --> + <string name="ag_homescreen_card_status_error">"Ðевалиден теÑÑ‚"</string> + <!-- XTXT: Homescreen card: status subtitle - invalid --> + <string name="ag_homescreen_card_status_invalid">"Вече не е валиден"</string> + <!-- XTXT: Homescreen card: status subtitle - findings --> + <string name="ag_homescreen_card_status_findings">"Резултати"</string> + <!-- XTXT: Homescreen card: subtitle - corona official name --> + <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> + <!-- XTXT: Homescreen card: status - negative --> + <string name="ag_homescreen_card_status_negative">"Отрицателен"</string> + <!-- XTXT: Homescreen card: status - positiv --> + <string name="ag_homescreen_card_status_positiv">"Положителен"</string> + + <!-- #################################### + Homescreen cards - common body + ###################################### --> + <!-- XTXT: homescreen card: body - result available --> + <string name="ag_homescreen_card_body_result_available">"Ðко резултатът от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚ за ÐºÐ¾Ñ€Ð¾Ð½Ð°Ð²Ð¸Ñ€ÑƒÑ Ðµ положителен, можете да предупредите оÑтаналите."</string> + <!-- XTXT: homescreen card: body - error --> + <string name="ag_homescreen_card_body_error">"Ðе може да бъде определен резултат от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚."</string> + <!-- XTXT: homescreen card: body - not valid test --> + <string name="ag_homescreen_card_body_not_valid_test">"ВашиÑÑ‚ теÑÑ‚ е направен преди повече от 21 дни и вече не е валиден. МолÑ, изтрийте го, за да можете да добавите друг Ñлед това. "</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_body_result_negative">"ÐÑмате поÑтавена диагноза за заразÑване Ñ Ð²Ð¸Ñ€ÑƒÑ SARS-CoV-2."</string> + <!-- XTXT: homescreen card: body - positive --> + <string name="ag_homescreen_card_body_result_positive">"Вашето заразÑване ÑÑŠÑ SARS-CoV-2 е потвърдено."</string> + + <!-- #################################### + Homescreen cards - rapid test + ###################################### --> + <!-- XHED: rapid test homescreen card: title --> + <string name="ag_homescreen_card_rapidtest_title">"Бърз теÑÑ‚"</string> + <!-- XBUT: rapid test homescreen card: dont show anymore button --> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Да не Ñе показва отново"</string> + <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"ТеÑÑ‚ÑŠÑ‚ вече не е актуален"</string> + <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Ðаправете PCR теÑÑ‚, за да потвърдите този резултат от теÑÑ‚a."</string> + + <!-- Body --> + <!-- XTXT: rapid test homescreen card: body - no result --> + <string name="ag_homescreen_card_rapidtest_body_no_result">"Резултатът от Ð’Ð°ÑˆÐ¸Ñ Ð±ÑŠÑ€Ð· теÑÑ‚ вÑе още не е определен."</string> + <!-- XTXT: rapid test homescreen card: body - outdated test --> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"БързиÑÑ‚ Ви теÑÑ‚ е на повече от 48 чаÑа и вече нÑма да Ñе показва тук."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_rapid_body_result_date">"Извършено на %1$s"</string> + + <!-- #################################### + Homescreen cards - PCR + ###################################### --> + <!-- XHED: PCR homescreen card: title --> + <string name="ag_homescreen_card_pcr_title">"PCR теÑÑ‚"</string> + <!-- XBUT: PCR homescreen card: clear test button --> + <string name="ag_homescreen_card_pcr_clear_test_button">"ИЗТРИВÐÐЕ ÐРТЕСТ"</string> + + <!-- Body --> + <!-- XTXT: PCR homescreen card: body - no result --> + <string name="ag_homescreen_card_pcr_body_no_result">"Резултатът от Ð’Ð°ÑˆÐ¸Ñ PCR теÑÑ‚ вÑе още не е определен."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_pcr_body_result_date">"Дата на региÑтриране на теÑта: %1$s"</string> + + <!-- XBUT: submission deletion warning button continue --> + <string name="submission_deletion_warning_continue_button">"Ðапред"</string> + <!-- XBUT: submission deletion warning button cancel --> + <string name="submission_deletion_warning_cancel_button">"Отказ"</string> + <!-- XHED: submission deletion warning title --> + <string name="submission_deletion_warning_title">"Бележка"</string> + <!-- YTXT: Headline for rapid test submission deletion warning --> + <string name="submission_deletion_warning_headline_antigen_test">"Вече Ñте региÑтрирали бърз теÑÑ‚."</string> + <!-- YTXT: Body for rapid test submission deletion warning --> + <string name="submission_deletion_warning_body_antigen_test">"Вече Ñте региÑтрирали бърз теÑÑ‚. Приложението може да обработва едновременно Ñамо един бърз теÑÑ‚ и един PCR теÑÑ‚. Ðко региÑтрирате друг бърз теÑÑ‚, предходниÑÑ‚ бърз теÑÑ‚ ще бъде изтрит."</string> + <!-- YTXT: Headline for PCR test submission deletion warning --> + <string name="submission_deletion_warning_headline_pcr_test">"Вече Ñте региÑтрирали PCR теÑÑ‚."</string> + <!-- YTXT: Body for PCR test submission deletion warning --> + <string name="submission_deletion_warning_body_pcr_test">"Вече Ñте региÑтрирали PCR теÑÑ‚. Приложението може да обработва едновременно Ñамо един бърз теÑÑ‚ и един PCR теÑÑ‚. Ðко региÑтрирате друг PCR теÑÑ‚, предходниÑÑ‚ PCR теÑÑ‚ ще бъде изтрит."</string> + + <!-- XHED: submission result antigen fragment toolbar text --> + <string name="submission_test_result_toolbar_text">"Резултатът от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚"</string> + <!-- XHED: submission result antigen fragment card title --> + <string name="submission_test_result_antigen_title">"Бърз теÑÑ‚"</string> + <!-- XTXT: submission result antigen fragment card "found" text --> + <string name="submission_result_subtitle">"Резултати"</string> + <!-- XTXT: submission test result fragment negative diagnosis --> + <string name="submission_test_result_negative">"Отрицателен"</string> + <!-- XTXT: submission test result fragment positive diagnosis --> + <string name="submission_test_result_positive">"Положителен"</string> + <!-- XTXT: submission result antigen fragment card patient name placeholder --> + <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> + <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"Дата на раждане %1$s"</string> + <!-- XTXT: coronatest negative antigen result time and date placeholder --> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Дата и Ñ‡Ð°Ñ Ð½Ð° издаване: %1$s, %2$s"</string> + <!-- XTXT: submission result antigen fragment card negative result message --> + <string name="submission_test_result_negative_message">"ÐÑмате поÑтавена диагноза за заразÑване Ñ Ð²Ð¸Ñ€ÑƒÑ SARS-CoV-2."</string> + <!-- XHED: submission result antigen fragment negative result proof title --> + <string name="submission_test_result_antigen_negative_proof_title">"Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð·Ð° проверка"</string> + <!-- XHED: submission result antigen fragment negative result proof body --> + <string name="submission_test_result_antigen_negative_proof_body">"Резултатът от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚ е поверителен и не Ñте задължени да го ÑподелÑте. Ð’ Ñлучаите, когато това Ñе изиÑква по закон, можете да да използвате който и да е от предоÑтавените начини."</string> + <!-- XHED: submission result antigen negative result counter title --> + <string name="submission_test_result_antigen_negative_counter_title">"Резултатът ще е доÑтъпен Ñлед"</string> + <!-- XHED: coronatest negative antigen result first info title --> + <string name="coronatest_negative_antigen_result_first_info_title">"БързиÑÑ‚ Ви теÑÑ‚ е добавен."</string> + <!-- XTXT: coronatest negative antigen result first info body --> + <string name="coronatest_negative_antigen_result_first_info_body">"Резултатът от теÑта ще Ñе показва тук в рамките на 48 чаÑа."</string> + <!-- XHED: coronatest negative antigen result second info title --> + <string name="coronatest_negative_antigen_result_second_info_title">"Отрицателен резултат"</string> + <!-- XTXT: coronatest negative antigen result second info body --> + <string name="coronatest_negative_antigen_result_second_info_body">"БързиÑÑ‚ теÑÑ‚ не потвърди , че имате ÐºÐ¾Ñ€Ð¾Ð½Ð°Ð²Ð¸Ñ€ÑƒÑ SARS-CoV-2."</string> + <!-- XTXT: coronatest negative antigen result third info title --> + <string name="coronatest_negative_antigen_result_third_info_title">"Премахване на теÑта"</string> + <!-- XTXT: coronatest negative antigen result third info body --> + <string name="coronatest_negative_antigen_restul_third_info_body">"МолÑ, изтрийте теÑта от приложението Corona-Warn-App, за да можете да запазите нов код на теÑÑ‚, ако е необходимо."</string> + + <!-- #################################### + Rapid Antigen Test Profile + ###################################### --> + <!-- XHED: Create RAT profile card title --> + <string name="rat_profile_create_card_title">"Създаване на профил за бързи теÑтове"</string> + <!-- XTXT: Create RAT profile card subtitle --> + <string name="rat_profile_create_card_subtitle">"Можете да Ñъздадете профил Ñ Ð»Ð¸Ñ‡Ð½Ð¸Ñ‚Ðµ Ñи данни, който да предÑтавÑте под формата на QR код при вÑеки бърз теÑÑ‚."</string> + <!-- XHED: Open RAT profile card title --> + <string name="rat_profile_open_card_title">"Профил за бързи теÑтове"</string> + <!-- XTXT: Open RAT profile card subtitle --> + <string name="rat_profile_open_card_subtitle">"СпеÑтете време като предÑтавите профила Ñи в пункта за теÑтване."</string> + <!-- XHED: Create RAT profile title --> + <string name="rat_profile_create_title">"Профил за бързи теÑтове"</string> + <!-- XTXT: Create RAT profile first name hint --> + <string name="rat_profile_create_first_name_hint">"Име"</string> + <!-- XTXT: Create RAT profile last name hint --> + <string name="rat_profile_create_last_name_hint">"ФамилиÑ"</string> + <!-- XTXT: Create RAT profile birth date hint --> + <string name="rat_profile_create_birth_date_hint">"Дата на раждане"</string> + <!-- XBUT: Create RAT profile save button --> + <string name="rat_profile_create_button">"Запазване"</string> + <!-- XTXT: Create RAT profile description --> + <string name="rat_profile_create_description">"Запазете личните Ñи данни като QR код, за да уÑкорите региÑтрациÑта в пунктовете за теÑтване."</string> + <!-- XTXT: Create RAT profile street hint --> + <string name="rat_profile_create_street_hint">"Улица и номер"</string> + <!-- XTXT: Create RAT profile post code hint --> + <string name="rat_profile_create_zip_code_hint">"ПощенÑки код"</string> + <!-- XTXT: Create RAT profile city hint --> + <string name="rat_profile_create_city_hint">"Град"</string> + <!-- XTXT: Create RAT profile phone hint --> + <string name="rat_profile_create_phone_hint">"Телефонен номер"</string> + <!-- XTXT: Create RAT profile email hint --> + <string name="rat_profile_create_email_hint">"Имейл адреÑ"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"Жена ÑÑŠÑ Ñмартфон в ръката Ñтои пред Ñграда. QR кодът Ñимволизира профила за бързи теÑтове, който Ñ‚Ñ€Ñбва да Ñе Ñканира."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"Запазете личните Ñи данни като QR код, за да уÑкорите региÑтрациÑта в пунктовете за теÑтване."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"Ðко Ñи Ñъздадете профил за бързи теÑтове, нÑма да Ñе налага да попълвате личните Ñи данни наново при вÑеки антигенен теÑÑ‚."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Ðапред"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"Подробна Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° обработването на данни и Вашето ÑъглаÑие"</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"ПредÑтавете този QR код в пункта за теÑтване, за да уÑкорите Ñнемането на личните Ñи данни. Дръжте и личната Ñи ката подръка."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"Дата на раждане %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Ðапред"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"Премахване"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"Желаете ли да премахнете профила Ñи за бързи теÑтове?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"МолÑ, имайте предвид, че ако го направите, този код повече нÑма да може да Ñе използва за региÑтрации."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"Изтриване"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Отказ"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml index df6c0cc91a0357dae644b9cbbd4b9a51454c0df9..bf594250652e02fbd751c788e07a7cfca7331544 100644 --- a/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml @@ -16,34 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ Ð½Ð° бързи антигенни теÑтове"</item> - <item>"Показване на доказателÑтво"</item> - <item>"Вече е доÑтъпна и гореща Ð»Ð¸Ð½Ð¸Ñ Ð·Ð° техничеÑки въпроÑи за Ð¾Ð±Ð°Ð¶Ð´Ð°Ð½Ð¸Ñ Ð¸Ð·Ð²ÑŠÐ½ териториÑта на ГерманиÑ"</item> - <item>"Вече е налична и гореща Ð»Ð¸Ð½Ð¸Ñ Ñ Ð¢ÐРкодове за Ð¾Ð±Ð°Ð¶Ð´Ð°Ð½Ð¸Ñ Ð¸Ð·Ð²ÑŠÐ½ ГерманиÑ"</item> + <item>"Водене на журнали за анализ на грешки"</item> + <item>"Създаване на профили за бързи теÑтове"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"ОÑвен резултатите от PCR теÑтовете, вече можете да показвате в приложението и резултатите от бързи антигенни теÑтове, Ñ ÐºÐ¾Ð¸Ñ‚Ð¾ да предупреждавате оÑтаналите."</item> - <item>"Ðко желаете, може да използвате приложението за доказване на Ð»Ð¸Ñ‡Ð½Ð¸Ñ Ð²Ð¸ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° заразÑване (например отрицателен бърз теÑÑ‚). Ðо имайте предвид, че по никакъв начин не Ñте задължени да използвате приложението за тази цел. Може да докажете ÑтатуÑа Ñи и по друг начин, Ñъобразен Ñ Ð¿Ñ€Ð°Ð²Ð½Ð¸Ñ‚Ðµ разпоредби на вашето мÑÑто на пребиваване."</item> - <item>"Вече имате доÑтъп до техничеÑката гореща Ð»Ð¸Ð½Ð¸Ñ Ð·Ð° Ð¾Ð±Ð°Ð¶Ð´Ð°Ð½Ð¸Ñ Ð¸Ð·Ð²ÑŠÐ½ Ð“ÐµÑ€Ð¼Ð°Ð½Ð¸Ñ Ð½Ð° номер +49 30 498 75401. Важат тарифите на Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð½Ð¸Ñ Ð²Ð¸ оператор."</item> - <item>"Ðко Ви е поÑтавена диагноза „коронавируÑ“, вече можете да заÑвите ТÐРкод извън Ð“ÐµÑ€Ð¼Ð°Ð½Ð¸Ñ Ð½Ð° номер +49 30 498 75402. Важат тарифите на Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð½Ð¸Ñ Ð²Ð¸ оператор."</item> + <item>"Вече можете да генерирате отчети за грешките чрез функциÑта за техничеÑка поддръжка. Ð’ Ñ‚ÑÑ… Ñе запиÑват дейÑтвиÑта Ви в приложението, за да можем по-леÑно да анализираме евентуални проблеми и да ги отÑтраним по-бързо."</item> + <item>"Вече можете да Ñи Ñъздадете профил Ñ Ð»Ð¸Ñ‡Ð½Ð¸Ñ‚Ðµ данни, необходими при бързи теÑтове. ИнформациÑта Ñе преобразува в QR код, който леÑно и бързо Ñе Ñканира в пунктовете за теÑтване."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> <item/> - <item/> - <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> <item/> - <item/> - <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/strings.xml b/Corona-Warn-App/src/main/res/values-bg/strings.xml index d6e4047261ad0a665bda816dac8a429dfdd65589..8da7d6f44c5b7628a77f2a193aa83fe8ba3dd5e7 100644 --- a/Corona-Warn-App/src/main/res/values-bg/strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/strings.xml @@ -64,6 +64,10 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"ИнÑталирано преди %s дни"</string> + <!-- XTXT: risk card - Days since installation if today --> + <string name="risk_card_body_installation_today">"ИнÑталирано днеÑ"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"ИнÑталирано вчера"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"РегиÑтрирането на Ð¸Ð·Ð»Ð°Ð³Ð°Ð½Ð¸Ñ Ð½Ð° риÑк е било активно през %1$s от изминалите 14 дни"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -864,10 +868,10 @@ <string name="debugging_debuglog_action_start_recording">"Ðачало"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Спиране и изтриване"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"Изпращане на отчет за грешки"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Локално запазване и продължаване"</string> + <string name="debugging_debuglog_action_local_log_export">"СподелÑне и напред"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"ЗапиÑването е активно"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -894,6 +898,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Продължаване на анализа"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"Ðеобходими Ñа поне 200 MB памет, за да Ñе Ñтартира анализа на грешките. МолÑ, оÑвободете мÑÑто."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"Подробна Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ñ‚Ð½Ð¾Ñно изпращането на отчети за грешки"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1041,6 +1046,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"Човек държи Ñмартфон. QR кодът на теÑта е кодът, който Ñ‚Ñ€Ñбва да бъде Ñканиран."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Вашето ÑъглаÑие е доброволно."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"Можете да получите резултата от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚, без да е необходимо да го ÑподелÑте, но ако Ñподелите резултата Ñи, ще помогнете на другите да Ñе предпазÑÑ‚ от заразÑване."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Вашата ÑамоличноÑÑ‚ нÑма да бъде разкрита. Другите потребители нÑма да узнаÑÑ‚ кой е Ñподелил резултата от теÑта Ñи."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"Ð’ раздела „Моите региÑтрации“ виждате ÑъбитиÑта и меÑтата, чиито региÑтрирани гоÑти ще бъдат предупредени. Можете да изтриете отделни региÑтрации от ÑпиÑъка, за да ги изключите от процеÑа на предупреждаване."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"За да дадете ÑъглаÑието Ñи, Ñ‚Ñ€Ñбва да имате навършени 16 години."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Резултат от теÑÑ‚"</string> @@ -1169,10 +1185,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Тъй като теÑÑ‚ÑŠÑ‚ Ви за ÐºÐ¾Ñ€Ð¾Ð½Ð°Ð²Ð¸Ñ€ÑƒÑ Ðµ положителен, можете да предупредите оÑтаналите чрез приложението."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"ФункциÑта за предупреждаване работи в нÑколко държави. Към момента това Ñа:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Тъй като Ви е поÑтавена диагноза „коронавируÑ“, можете да предупредите оÑтаналите Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰Ñ‚Ð° на приложението. Ðко Ñте направили бърз теÑÑ‚, функциÑта за предупреждаване ще работи Ñамо в ГерманиÑ. Ðко Ñте направили PCR теÑÑ‚, Ñ‚Ñ Ñ‰Ðµ работи в Ñледните държави:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1988,9 +2001,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"Ðевалиден QR код"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"СканираниÑÑ‚ QR код е невалиден.\nМолÑ, Ñканирайте валиден QR код.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"СканираниÑÑ‚ QR код не е валиден за региÑтрации за Ñъбитие или мÑÑто. \nМолÑ, Ñканирайте подходÑщ QR код. \n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"ОК"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Отказ"</string> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Предупреждение за неÑъвмеÑтимоÑÑ‚"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Вашето мобилно уÑтройÑтво може да поучава извеÑÑ‚Ð¸Ñ Ð·Ð° COVID-19 чрез Bluetooth, но не може да изпраща такива. Това означава, че Вие ще получавате предупреждениÑ, но без да извеÑÑ‚Ñвате околните, че Ñа били в контакт Ñ Ð²Ð°Ñ. Можете както да получавате, така и да изпращате Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² ÑледÑтвие на региÑтрации в дадена локациÑ."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Вашето мобилно уÑтройÑтво не може нито да изпраща, нито да получава извеÑÑ‚Ð¸Ñ Ð·Ð° COVID-19 чрез Bluetooth. Можете както да получавате, така и да изпращате Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² ÑледÑтвие на региÑтрации в дадена локациÑ."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml index f2e027c2a9a17213c03e8daee236621e6d269c81..ef5e088f16b90a5691fadae4c21bf406caf926a0 100644 --- a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml @@ -121,13 +121,13 @@ <!-- XHED: submission result antigen fragment negative result proof title --> <string name="submission_test_result_antigen_negative_proof_title">"Nachweis-Funktion"</string> <!-- XHED: submission result antigen fragment negative result proof body --> - <string name="submission_test_result_antigen_negative_proof_body">"Sie können den hier angezeigten Befund auch als Nachweis für das Vorliegen eines negativen Schnelltest-Ergebnisses verwenden. Informieren Sie sich hierzu bitte auch über die Kriterien für die Anerkennung von Test-Nachweisen in Ihrem Bundesland. Bitte beachten Sie, dass Sie grundsätzlich nicht zum Nachweis per App verpflichtet sind. Sie können Ihr Schnelltest-Ergebnis im Rahmen der rechtlichen Bestimmungen auf andere Weise nachweisen."</string> + <string name="submission_test_result_antigen_negative_proof_body">"Sie können den hier angezeigten Befund auch als Nachweis für das Vorliegen eines negativen Schnelltest-Ergebnisses verwenden.\n\nBitte beachten Sie, dass Sie nur dann einen Nachweis über Ihr Schnelltest-Ergebnis erbringen müssen, wenn dies gesetzlich festgelegt ist. Sie können den Nachweis über die App oder auch auf andere Weise erbringen. Informieren Sie sich hierzu bitte auch über die Kriterien für die Anerkennung von Test-Nachweisen in Ihrem Bundesland."</string> <!-- XHED: submission result antigen negative result counter title --> <string name="submission_test_result_antigen_negative_counter_title">"Ergebnis liegt vor seit"</string> <!-- XHED: coronatest negative antigen result first info title --> <string name="coronatest_negative_antigen_result_first_info_title">"Ihr Schnelltest wurde hinzugefügt."</string> <!-- XTXT: coronatest negative antigen result first info body --> - <string name="coronatest_negative_antigen_result_first_info_body">"Das Test-Ergebnis wird 48 Stunden hier angezeigt. Zusätzlich legt die App einen Eintrag in Ihrem Kontakt-Tagebuch an."</string> + <string name="coronatest_negative_antigen_result_first_info_body">"Das Test-Ergebnis wird 48 Stunden hier angezeigt."</string> <!-- XHED: coronatest negative antigen result second info title --> <string name="coronatest_negative_antigen_result_second_info_title">"Befund negativ"</string> <!-- XTXT: coronatest negative antigen result second info body --> @@ -170,4 +170,31 @@ <string name="rat_profile_create_phone_hint">Telefonnummer</string> <!-- XTXT: Create RAT profile email hint --> <string name="rat_profile_create_email_hint">E-Mail-Adresse</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">Eine Frau mit einem Smartphone in der Hand steht vor einem Gebäude. Ein QR-Code symbolisiert das zu scannende Schnelltest-Profil.</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">Legen Sie Ihre persönlichen Daten als QR-Code ab, um die Registrierung an der Teststelle zu beschleunigen.</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">Mit Ihrem eigenen Schnelltest-Profil müssen Sie Ihre persönlichen Daten nicht bei jedem neuen Schnelltest erneut ausfüllen.</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">Weiter</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">Ausführliche Informationen zur Datenverarbeitung und Ihrem Einverständnis.</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">Bitte zeigen Sie diesen QR-Code an der Teststelle vor, um Ihre persönlichen Daten schnell erfassen zu lassen. Bitte halten zusätzlich Sie Ihren Personalausweis bereit.</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">geboren %1$s</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">Weiter</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">Entfernen</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">Wollen Sie das Schnelltest-Profil wirklich entfernen?</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">Bitte denken Sie daran, dass der QR-Code danach nicht mehr zum Registrieren verwendet werden kann.</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">Löschen</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">Abbrechen</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml index 758b6ca6e8c166dd4540c89b14c42010f5ffb433..6c137b480c2df2a490925c815e243d5578da1ecc 100644 --- a/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/event_registration_strings.xml @@ -175,9 +175,9 @@ <!-- XBUT: Event organiser qr codes list: new qr code button --> <string name="trace_location_organiser_list_new_button">"QR-Code"</string> <!-- XTXT: Event organiser qr codes list: new qr code button --> - <string name="trace_location_organiser_list_no_codes_title">"Noch keine QR Codes erstellt"</string> + <string name="trace_location_organiser_list_no_codes_title">"Noch keine QR-Codes erstellt"</string> <!-- XTXT: Event organiser qr codes list: new qr code button --> - <string name="trace_location_organiser_list_no_codes_subtitle">"Hier werden alle QR-Codes angezeigt, die Sie für ein Ort oder Event erstellt haben. Sie können die QR-Codes löschen, wenn diese nicht mehr verwendet werden sollen."</string> + <string name="trace_location_organiser_list_no_codes_subtitle">"Hier werden alle QR-Codes angezeigt, die Sie für einen Ort oder ein Event erstellt haben. Sie können die QR-Codes löschen, wenn diese nicht mehr verwendet werden sollen."</string> <!-- XBUT: Event organiser qr codes list: menu: information button --> <string name="trace_location_organizer_list_menu_information_btn">"Informationen"</string> diff --git a/Corona-Warn-App/src/main/res/values-de/legal_strings.xml b/Corona-Warn-App/src/main/res/values-de/legal_strings.xml index c262665e2bac9319a4d9cafc676251776d4ea1aa..890426923a65f9ad821c8327e26950bb89810963 100644 --- a/Corona-Warn-App/src/main/res/values-de/legal_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/legal_strings.xml @@ -31,13 +31,15 @@ <string name="submission_your_consent_agreement_share_symptoms">"<b>Im Falle von Schnelltests werden nur Nutzer der Corona-Warn-App gewarnt. Wenn Sie zusätzlich Angaben zum Beginn Ihrer Symptome machen, werden auch diese Daten geteilt.</b>\n\nSie können Ihr Einverständnis zurücknehmen, indem Sie oben „Andere warnen“ deaktivieren."</string> <!-- YTXT: Body for your consent screen agreement share symptoms with additional hint for test result--> - <string name="submission_your_consent_agreement_share_symptoms_2"><b>Wenn Sie zusätzlich Angaben zum Beginn Ihrer Symptome machen, werden auch diese geteilt.</b>\n\nSie können Ihr Einverständnis zurücknehmen, indem Sie oben „Andere warnen“ deaktivieren. Ihr Testergebnis wird Ihnen anschließend angezeigt.</string> + <string name="submission_your_consent_agreement_share_symptoms_2"><b>Im Falle von Schnelltests werden nur Nutzer der Corona-Warn-App gewarnt. Wenn Sie zusätzlich Angaben zum Beginn Ihrer Symptome machen, werden auch diese Daten geteilt.</b>\n\nSie können Ihr Einverständnis zurücknehmen, indem Sie oben „Andere warnen“ deaktivieren. Ihr Testergebnis wird Ihnen anschließend angezeigt.</string> <!-- YTXT: Body for keys submission no consent text first part--> <string name="submission_no_consent_your_consent_subsection_body_first_part">"Durch Antippen von „Einverstanden“ willigen Sie wie folgt ein:"</string> <!-- YTXT: Body for keys submission no consent text second part--> - <string name="submission_no_consent_your_consent_subsection_body_second_part">"<b>Die App teilt Ihr Testergebnis, um Nutzer von offiziellen Corona-Apps, denen Sie begegnet sind, zu warnen. Die Warnung funktioniert in den oben genannten Ländern. Wenn Sie zusätzlich Angaben zum Beginn Ihrer Symptome machen, werden auch diese geteilt.</b> Bitte geben Sie Ihr Testergebnis (genauer gesagt: Ihre Zufalls-IDs der letzten 14 Tage) im nächsten Schritt frei, indem Sie „Teilen“ antippen."</string> + <string name="submission_no_consent_your_consent_subsection_body_second_part">"<b>Die App teilt Ihr Testergebnis, um Nutzer, denen Sie begegnet sind, zu warnen. Dies betrifft Nutzer von Corona-Apps der oben genannten Länder und Nutzer, die zeitgleich am selben Event oder Ort wie Sie eingecheckt waren. Im Falle von Schnelltests werden nur Nutzer der Corona-Warn-App gewarnt. Wenn Sie zusätzlich Angaben zum Beginn Ihrer Symptome machen, werden auch diese Daten geteilt.</b>"</string> <!-- YTXT: Body for keys submission no consent text third part--> - <string name="submission_no_consent_your_consent_subsection_body_third_part">"Sie können Ihr Einverständnis nach bereits erfolgter Warnung zurücknehmen, indem Sie die App löschen."</string> + <string name="submission_no_consent_your_consent_subsection_body_third_part">"Erteilen Sie im nächsten Schritt bitte Ihre Erlaubnis zum Zugriff auf die Zufalls-IDs."</string> + <!-- YTXT: Body for keys submission no consent text fourth part--> + <string name="submission_no_consent_your_consent_subsection_body_fourth_part">"Sie können Ihr Einverständnis nach bereits erfolgter Warnung zurücknehmen, indem Sie die App löschen."</string> <!-- XTXT: Title for legal information of the contact diary onboarding screen --> <string name="contact_diary_onboarding_privacy_information_title">Hinweise zur Datenverarbeitung</string> @@ -147,4 +149,22 @@ <string name="trace_location_onboarding_body_consent5">Falls Sie später selbst eine Warnung auslösen, werden automatisch alle anderen Nutzer, die zeitgleich mit Ihnen bei einem unter „Meine Check-ins“ eingetragenen Event oder Ort eingecheckt waren, anhand der Event-ID gewarnt.</string> <!-- YMSG: Onboarding trace location bullet points --> <string name="trace_location_onboarding_body_consent6">Unter „Meine Check-ins“ können Sie einen Check-in jederzeit löschen. In diesem Fall werden Sie nur gewarnt, wenn Ihr Smartphone die Zufalls-IDs des positiv getesteten Nutzers empfangen und eine Risiko-Begegnung ermittelt hat.</string> + + <!-- RAT Profile Onboarding --> + <!-- XHED: Title for RAT Profile onboarding consent card --> + <string name="rat_profile_onboarding_card_title" translatable="false">Datenschutz und Datensicherheit</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent1" translatable="false">Das Anlegen eines Schnelltest-Profils in der App und dessen Verwendung sind freiwillig.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent2" translatable="false">Die App wandelt die Daten, die Sie für Ihr Schnelltest-Profil eingeben, in Ihren persönlichen QR-Code um. Im QR-Code sind dann alle eingegebenen Daten enthalten.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent3" translatable="false">In der Teststelle können Sie den QR-Code Ihres Schnelltest-Profils scannen lassen, um der Teststelle Ihre Daten schnell und sicher bereitzustellen. Die Teststelle benötigt Ihre Daten zur Durchführung des Tests. Sie können die Daten aber auch ohne die App bereitstellen. Sie entscheiden selbst, welche Daten im QR-Code enthalten sind und welche Sie der Teststelle auf andere Weise mitteilen.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent4" translatable="false">Auf Ihr persönliches Schnelltest-Profil haben zunächst nur Sie Zugriff. Nur wenn Sie den QR-Code des Schnelltest-Profils anderen Personen zeigen, können diese Zugriff auf die persönlichen Daten Ihres Schnelltest-Profils erhalten. Ein Zugriff auf andere Daten aus der App erfolgt dabei nicht (z.B. Ihre Check-ins oder Angaben im Kontakt-Tagebuch).</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent5" translatable="false">Sie haben jederzeit die Möglichkeit, Ihr Schnelltest-Profil wieder zu entfernen. Bis dahin bleibt es in der App auf Ihrem Smartphone gespeichert.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent6" translatable="false">Der QR-Code enthält Ihre persönlichen Angaben. Stellen Sie Ihren persönlichen QR-Code niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten ausgelesen werden. Wir empfehlen Ihren persönlichen QR-Code nicht zu veröffentlichen und nicht per E-Mail zu versenden.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent7" translatable="false">Private Personen oder Unternehmen dürfen von Ihnen nicht verlangen, dass Sie Ihr Schnelltest-Profil vorzeigen.</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml index 93fbc3f28308b362079b781d37bc2115ea87ae06..04d61acf73a0ee7b5153408f3cdc6a2b35e86b18 100644 --- a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml @@ -23,7 +23,7 @@ <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>Sie können nun im Rahmen des technischen Supports für die App einen Fehlerprotokoll erstellen, das die Schritte aufzeichnet, die Sie in der App ausführen. Somit wird die Analyse von möglichen Fehlern erleichtert, welches zu einer schnelleren Fehlerbehebung führen kann.</item> + <item>Sie können nun im Rahmen des technischen Supports für die App ein Fehlerprotokoll erstellen, das die Schritte aufzeichnet, die Sie in der App ausführen. Somit wird die Analyse von möglichen Fehlern erleichtert, welches zu einer schnelleren Fehlerbehebung führen kann.</item> <item>Sie können nun ein Schnelltest-Profil mit Ihren persönlichen Daten anlegen. Ihr Profil kann dann an Teststellen per QR-Code schnell und einfach gescannt werden.</item> </string-array> diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 5a269d05f838f751f88531439d414c92d5dd3896..efad8444474ecdc32c8751b2e87af4c91957c731 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -854,7 +854,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"Um den technischen Support der App bei der Fehleranalyse zu unterstützen, können Sie einen Fehlerbericht der CWA aufzeichnen. Hierbei werden die einzelnen technischen Schritte und Ereignisse beim Ablauf der App detailliert aufgezeichnet. Den Fehlerbericht können Sie dann an den technischen Support senden und so helfen, Fehler zu erkennen und zu beheben."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"Weitere Informationen finden Sie in den FAQ:\nFAQ zu den Fehlerberichten"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"Weitere Informationen finden Sie in den FAQ: FAQ zu den Fehlerberichten"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"FAQ zu den Fehlerberichten"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -1047,6 +1047,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"Eine Person hält ein Smartphone. Ein QR-Code auf einem Test symbolisiert den zu scannenden Code."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Ihr Einverständnis ist freiwillig."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"Sie können Ihr Testergebnis auch abrufen, wenn Sie dies nicht teilen. Wenn Sie ihr Testergebnis teilen, helfen Sie jedoch mit, Ihre Mitmenschen vor Ansteckungen zu schützen."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Ihre Identität bleibt geheim. Andere Nutzer erfahren nicht, wer sein Testergebnis geteilt hat."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"Unter „Meine Check-ins“ können Sie Ihre Events und Orte einsehen, deren eingecheckte Gäste gewarnt werden. Sie können einzelne Check-ins auch entfernen und so von der Warnung ausschließen."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"Sie können Ihr Einverständnis abgeben, wenn Sie mindestens 16 Jahre alt sind."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Testergebnis"</string> @@ -1175,10 +1186,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Da Sie positiv auf Corona getestet wurden, können Sie Ihre Mitmenschen über die App warnen."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"Die Warnung funktioniert in mehreren Ländern. Derzeit nehmen folgende Länder teil:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Da Sie positiv auf Corona getestet wurden, können Sie Ihre Mitmenschen über die App warnen. Bei einem Schnelltest funktioniert die Warnung nur in Deutschland, im Falle eines PCR-Tests funktioniert die Warnung in folgenden Ländern:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1994,9 +2002,9 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">QR-Code nicht gültig</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">Der gescannte QR-Code ist nicht gültig.\nBitte scannen Sie einen gültigen QR-Code.\n\n(%1$s)</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">Der gescannte QR-Code gilt nicht für das Einchecken für ein Event oder einen Ort. \nBitte scannen Sie einen geeigneten QR-Code.\n\n(%1$s)</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> - <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">Ok</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">OK</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">Abbrechen</string> @@ -2004,17 +2012,21 @@ Incompatibility warning card ###################################### --> + <!-- XHED: Incompitability card title --> <string name="incompatible_headline">Inkompatibilitätswarnung</string> + <!-- XTXT: Incompitability card content --> <string name="incompatible_advertising_not_supported">Ihr Smartphone kann COVID-19-Benachrichtigungen über die Bluetooth-Schnittstelle lediglich empfangen, jedoch nicht versenden. Das heißt, dass Sie über diese Schnittstelle von Risiko-Begegnungen gewarnt werden können, jedoch nicht selbst warnen können. Warnungen, die im Rahmen von Check-ins erfolgen, können Sie sowohl empfangen als auch selbst versenden. </string> + <!-- XTXT: Incompitability card content --> <string name="incompatible_scanning_not_supported">Ihr Smartphone kann COVID-19-Benachrichtigungen über die Bluetooth-Schnittstelle weder empfangen noch senden. Warnungen, die im Rahmen von Check-ins erfolgen, können Sie sowohl empfangen als auch selbst versenden. </string> + <!-- XTXT: Incompitability faq link --> <string name="incompatible_link">"https://www.coronawarn.app/de/faq/#incompatibility_warning"</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f8f1e06cab846bc72146e8817815dc8e2900d89c --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + + <!-- #################################### + Homescreen cards - register test card + ###################################### --> + <!-- XHED: Register test homescreen card: title --> + <string name="ag_homescreen_card_test_register_title">"Register Your Test"</string> + <!-- XTXT: Register test homescreen card: body --> + <string name="ag_homescreen_card_test_register_body">"Use the app to retrieve your test results, so you can warn others more quickly."</string> + + <!-- #################################### + Homescreen cards - common buttons + ###################################### --> + <!-- XBUT: Homescreen card: show test button --> + <string name="ag_homescreen_card_show_button">"Display Test"</string> + <!-- XBUT: Homescreen card: warn others button --> + <string name="ag_homescreen_card_warn_others_button">"Warn Others"</string> + + <!-- #################################### + Homescreen cards - common status + ###################################### --> + <!-- XTXT: Homescreen card: status subtitle - no result --> + <string name="ag_homescreen_card_status_no_result">"Your result is not available yet"</string> + <!-- XTXT: Homescreen card: status subtitle - result available --> + <string name="ag_homescreen_card_status_result_available">"Retrieve Test Result"</string> + <!-- XTXT: Homescreen card: status subtitle - error --> + <string name="ag_homescreen_card_status_error">"Invalid Test"</string> + <!-- XTXT: Homescreen card: status subtitle - invalid --> + <string name="ag_homescreen_card_status_invalid">"No Longer Valid"</string> + <!-- XTXT: Homescreen card: status subtitle - findings --> + <string name="ag_homescreen_card_status_findings">"Findings"</string> + <!-- XTXT: Homescreen card: subtitle - corona official name --> + <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> + <!-- XTXT: Homescreen card: status - negative --> + <string name="ag_homescreen_card_status_negative">"Negative"</string> + <!-- XTXT: Homescreen card: status - positiv --> + <string name="ag_homescreen_card_status_positiv">"Positive"</string> + + <!-- #################################### + Homescreen cards - common body + ###################################### --> + <!-- XTXT: homescreen card: body - result available --> + <string name="ag_homescreen_card_body_result_available">"If you have tested positive for coronavirus, you can warn others."</string> + <!-- XTXT: homescreen card: body - error --> + <string name="ag_homescreen_card_body_error">"Your test could not be evaluated."</string> + <!-- XTXT: homescreen card: body - not valid test --> + <string name="ag_homescreen_card_body_not_valid_test">"Your test is more than 21 days old and is therefore no longer relevant. Please delete the test. You can then add another. "</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_body_result_negative">"You have not been diagnosed with the SARS-CoV-2 virus."</string> + <!-- XTXT: homescreen card: body - positive --> + <string name="ag_homescreen_card_body_result_positive">"You have been diagnosed with the SARS-CoV-2 virus."</string> + + <!-- #################################### + Homescreen cards - rapid test + ###################################### --> + <!-- XHED: rapid test homescreen card: title --> + <string name="ag_homescreen_card_rapidtest_title">"Rapid Test"</string> + <!-- XBUT: rapid test homescreen card: dont show anymore button --> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Don’t Display Again"</string> + <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Test No Longer Current"</string> + <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Take a PCR test to verify this test result."</string> + + <!-- Body --> + <!-- XTXT: rapid test homescreen card: body - no result --> + <string name="ag_homescreen_card_rapidtest_body_no_result">"The evaluation of your rapid test is not done yet."</string> + <!-- XTXT: rapid test homescreen card: body - outdated test --> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Your rapid test is more than 48 hours old and will no longer be displayed here."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_rapid_body_result_date">"Carried out on %1$s"</string> + + <!-- #################################### + Homescreen cards - PCR + ###################################### --> + <!-- XHED: PCR homescreen card: title --> + <string name="ag_homescreen_card_pcr_title">"PCR Test"</string> + <!-- XBUT: PCR homescreen card: clear test button --> + <string name="ag_homescreen_card_pcr_clear_test_button">"DELETE TEST"</string> + + <!-- Body --> + <!-- XTXT: PCR homescreen card: body - no result --> + <string name="ag_homescreen_card_pcr_body_no_result">"The evaluation of your PCR test is not done yet."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_pcr_body_result_date">"Test registered on %1$s"</string> + + <!-- XBUT: submission deletion warning button continue --> + <string name="submission_deletion_warning_continue_button">"Continue"</string> + <!-- XBUT: submission deletion warning button cancel --> + <string name="submission_deletion_warning_cancel_button">"Cancel"</string> + <!-- XHED: submission deletion warning title --> + <string name="submission_deletion_warning_title">"Note"</string> + <!-- YTXT: Headline for rapid test submission deletion warning --> + <string name="submission_deletion_warning_headline_antigen_test">"You have already registered a rapid test."</string> + <!-- YTXT: Body for rapid test submission deletion warning --> + <string name="submission_deletion_warning_body_antigen_test">"You have already registered a rapid test. The app can manage a maximum of one rapid test and one PCR test at the same time. If you register another rapid test, the first rapid test will be deleted from the app."</string> + <!-- YTXT: Headline for PCR test submission deletion warning --> + <string name="submission_deletion_warning_headline_pcr_test">"You have already registered a PCR test."</string> + <!-- YTXT: Body for PCR test submission deletion warning --> + <string name="submission_deletion_warning_body_pcr_test">"You have already registered a PCR test. The app can manage a maximum of one rapid test and one PCR test at the same time. If you register another PCR test, the first PCR test will be deleted from the app."</string> + + <!-- XHED: submission result antigen fragment toolbar text --> + <string name="submission_test_result_toolbar_text">"Your Test Result"</string> + <!-- XHED: submission result antigen fragment card title --> + <string name="submission_test_result_antigen_title">"Rapid Test"</string> + <!-- XTXT: submission result antigen fragment card "found" text --> + <string name="submission_result_subtitle">"Findings"</string> + <!-- XTXT: submission test result fragment negative diagnosis --> + <string name="submission_test_result_negative">"Negative"</string> + <!-- XTXT: submission test result fragment positive diagnosis --> + <string name="submission_test_result_positive">"Positive"</string> + <!-- XTXT: submission result antigen fragment card patient name placeholder --> + <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> + <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"Born %1$s"</string> + <!-- XTXT: coronatest negative antigen result time and date placeholder --> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Issued: %1$s, %2$s"</string> + <!-- XTXT: submission result antigen fragment card negative result message --> + <string name="submission_test_result_negative_message">"You have not been diagnosed with the SARS-CoV-2 virus."</string> + <!-- XHED: submission result antigen fragment negative result proof title --> + <string name="submission_test_result_antigen_negative_proof_title">"Verification Feature"</string> + <!-- XHED: submission result antigen fragment negative result proof body --> + <string name="submission_test_result_antigen_negative_proof_body">"Your test result is private, although some laws may require you to share it. When required, you may show it via any of the ways provided."</string> + <!-- XHED: submission result antigen negative result counter title --> + <string name="submission_test_result_antigen_negative_counter_title">"Result available since"</string> + <!-- XHED: coronatest negative antigen result first info title --> + <string name="coronatest_negative_antigen_result_first_info_title">"Your rapid test was added."</string> + <!-- XTXT: coronatest negative antigen result first info body --> + <string name="coronatest_negative_antigen_result_first_info_body">"The test result is displayed here for 48 hours."</string> + <!-- XHED: coronatest negative antigen result second info title --> + <string name="coronatest_negative_antigen_result_second_info_title">"Negative Diagnosis"</string> + <!-- XTXT: coronatest negative antigen result second info body --> + <string name="coronatest_negative_antigen_result_second_info_body">"The rapid test provided no evidence that you have coronavirus SARS-CoV-2."</string> + <!-- XTXT: coronatest negative antigen result third info title --> + <string name="coronatest_negative_antigen_result_third_info_title">"Remove Test"</string> + <!-- XTXT: coronatest negative antigen result third info body --> + <string name="coronatest_negative_antigen_restul_third_info_body">"Please delete the test from the Corona-Warn-App, so that you can save a new test code here if necessary."</string> + + <!-- #################################### + Rapid Antigen Test Profile + ###################################### --> + <!-- XHED: Create RAT profile card title --> + <string name="rat_profile_create_card_title">"Create Rapid Test Profile"</string> + <!-- XTXT: Create RAT profile card subtitle --> + <string name="rat_profile_create_card_subtitle">"You can create a profile with your personal data, which you can submit via QR code during every rapid test."</string> + <!-- XHED: Open RAT profile card title --> + <string name="rat_profile_open_card_title">"Rapid Test Profile"</string> + <!-- XTXT: Open RAT profile card subtitle --> + <string name="rat_profile_open_card_subtitle">"Save time and present your rapid test profile at the testing point."</string> + <!-- XHED: Create RAT profile title --> + <string name="rat_profile_create_title">"Rapid Test Profile"</string> + <!-- XTXT: Create RAT profile first name hint --> + <string name="rat_profile_create_first_name_hint">"First Name"</string> + <!-- XTXT: Create RAT profile last name hint --> + <string name="rat_profile_create_last_name_hint">"Last Name"</string> + <!-- XTXT: Create RAT profile birth date hint --> + <string name="rat_profile_create_birth_date_hint">"Date of Birth"</string> + <!-- XBUT: Create RAT profile save button --> + <string name="rat_profile_create_button">"Save"</string> + <!-- XTXT: Create RAT profile description --> + <string name="rat_profile_create_description">"Save your personal data as a QR code to speed up registration at testing points."</string> + <!-- XTXT: Create RAT profile street hint --> + <string name="rat_profile_create_street_hint">"Street and House Number"</string> + <!-- XTXT: Create RAT profile post code hint --> + <string name="rat_profile_create_zip_code_hint">"Postal Code"</string> + <!-- XTXT: Create RAT profile city hint --> + <string name="rat_profile_create_city_hint">"City"</string> + <!-- XTXT: Create RAT profile phone hint --> + <string name="rat_profile_create_phone_hint">"Phone Number"</string> + <!-- XTXT: Create RAT profile email hint --> + <string name="rat_profile_create_email_hint">"E-Mail Address"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"A woman with a smartphone in her hand is standing in front of a building. A QR code symbolizes the rapid test profile to be scanned."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"Save your personal data as a QR code to speed up registration at testing points."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"When you create a rapid test profile, you will not have to fill out your personal data again before every rapid test."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Continue"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"Detailed Information on Data Processing and Your Consent"</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"Please present this QR code at the testing point so your personal data can be recorded more quickly. Also keep your ID card at hand."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"Born %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Continue"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"Remove"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"Do you want to remove the rapid test profile?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"Please consider that if you do, the QR code can no longer be used for registration."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"Delete"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Cancel"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml index b311a1fc84dda9b1d93cd5f7dab78081a6031a48..46730e89a2f7de86a8d75d92998a4651f4d70458 100644 --- a/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml @@ -16,34 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Registration of Rapid Antigen Tests"</item> - <item>"Display of Proof"</item> - <item>"Technical Hotline Now Available from Outside Germany"</item> - <item>"TAN Hotline Now Available from Outside Germany"</item> + <item>"Record Troubleshooting Logs"</item> + <item>"Create Rapid Test Profiles"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"In addition to the results of PCR tests, you can now also display the results of rapid antigen tests in the app and use them to warn others."</item> - <item>"If you want, you can use the app to prove your personal infection status (a negative rapid test, for example). Please note, however, that you are under no obligation to use the app to prove your infection status. You can also prove your infection status in another manner compliant with the legal regulations at your residence."</item> - <item>"You can now reach the technical hotline from outside Germany as well, under the number +49 30 498 75401. The fees from your telephone provider will apply."</item> - <item>"If you are diagnosed with coronavirus, you can now request a TAN from outside Germany, under the number +49 30 498 75402. The fees from your telephone provider will apply."</item> + <item>"You can now generate an error log in the technical support feature for the app. It records the steps that you perform in the app, to simplify the analysis of potential errors and help us correct them more quickly."</item> + <item>"You can now create a rapid test profile in your personal data. Your profile can then be scanned at testing points quickly and easily via QR code."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> <item/> - <item/> - <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> <item/> - <item/> - <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index 190cd0d62a392b7ba3bb2f013052aa2acc7ebcd8..9dc5674d28d56b3a353c27987576a4f6b80883d0 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -64,6 +64,10 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"Installed %s days ago"</string> + <!-- XTXT: risk card - Days since installation if today --> + <string name="risk_card_body_installation_today">"Installed today"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"Installed yesterday"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"Exposure logging was active for %1$s of the past 14 days"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -849,7 +853,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"To help the app technical support team with error analysis, you can record an error report from the CWA. When you do so, the individual technical steps and results of app processes are recorded. You can then send the error report to technical support and help to identify and correct the error."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"For further information, please see our FAQ page:\nFAQ for error reports"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"For further information, please see our FAQ page: FAQ for error reports"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"FAQ for error reports"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -864,10 +868,10 @@ <string name="debugging_debuglog_action_start_recording">"Start"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Stop and Delete"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"Send Error Report"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Save Locally and Continue"</string> + <string name="debugging_debuglog_action_local_log_export">"Share and Continue"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"Recording Active"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -894,6 +898,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Continue Analysis"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"You need at least 200 MB of memory to start the error analysis. Please free up memory."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"Detailed Information on Sending Error Reports"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1041,6 +1046,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"A person is holding a smartphone. A QR code on a test symbolizes the code to be scanned."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Your consent is voluntary."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"You can retrieve your test result even if you do not choose to share it. But if you share your test result, you will help to protect others against infection."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Your identity will remain secret. Other users will not find out who has shared a test result."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"Under “My Check-Insâ€, you can see the events and places whose checked-in guests will be warned. You can remove individual check-ins from the list to exclude them from the warning process."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"You must be at least 16 years old to grant your consent."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Test Result"</string> @@ -1169,10 +1185,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Because you tested positive for coronavirus, you can now warn others through the app."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"The warning feature works in several countries. The following countries are currently participating:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Because you have been diagnosed with coronavirus, you can warn others through the app. If you took a rapid test, the warning feature only works in Germany. If you took a PCR test, the warning feature works in the following countries:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1988,9 +2001,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"Invalid QR Code"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"The scanned QR code is not valid.\nPlease scan a valid QR code.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"The scanned QR code is not valid for check-ins for an event or place. \nPlease scan a suitable QR code.\n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"OK"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Cancel"</string> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Incompatibility Warning"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Your smartphone can only receive COVID-19 notifications via Bluetooth, but not send them. This means you can be warned of exposures through this interface, but you cannot warn others of exposures yourself. You can send and receive warnings that result from check-ins."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Your smartphone cannot send or receive COVID-19 notifications via Bluetooth. You can send and receive warnings that result from check-ins."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a8bc292e6f2839aae216bdd19828c011f27fd62 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + + <!-- #################################### + Homescreen cards - register test card + ###################################### --> + <!-- XHED: Register test homescreen card: title --> + <string name="ag_homescreen_card_test_register_title">"Zarejestruj test"</string> + <!-- XTXT: Register test homescreen card: body --> + <string name="ag_homescreen_card_test_register_body">"Korzystaj z aplikacji do pobierania wyników swoich badaÅ„, aby szybciej ostrzegać innych."</string> + + <!-- #################################### + Homescreen cards - common buttons + ###################################### --> + <!-- XBUT: Homescreen card: show test button --> + <string name="ag_homescreen_card_show_button">"WyÅ›wietl test"</string> + <!-- XBUT: Homescreen card: warn others button --> + <string name="ag_homescreen_card_warn_others_button">"Ostrzegaj innych"</string> + + <!-- #################################### + Homescreen cards - common status + ###################################### --> + <!-- XTXT: Homescreen card: status subtitle - no result --> + <string name="ag_homescreen_card_status_no_result">"Wynik Twojego testu nie jest jeszcze dostÄ™pny"</string> + <!-- XTXT: Homescreen card: status subtitle - result available --> + <string name="ag_homescreen_card_status_result_available">"Pobierz wynik testu"</string> + <!-- XTXT: Homescreen card: status subtitle - error --> + <string name="ag_homescreen_card_status_error">"NieprawidÅ‚owy test"</string> + <!-- XTXT: Homescreen card: status subtitle - invalid --> + <string name="ag_homescreen_card_status_invalid">"Utrata ważnoÅ›ci"</string> + <!-- XTXT: Homescreen card: status subtitle - findings --> + <string name="ag_homescreen_card_status_findings">"Wyniki"</string> + <!-- XTXT: Homescreen card: subtitle - corona official name --> + <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> + <!-- XTXT: Homescreen card: status - negative --> + <string name="ag_homescreen_card_status_negative">"Brak zakażenia"</string> + <!-- XTXT: Homescreen card: status - positiv --> + <string name="ag_homescreen_card_status_positiv">"Zakażenie"</string> + + <!-- #################################### + Homescreen cards - common body + ###################################### --> + <!-- XTXT: homescreen card: body - result available --> + <string name="ag_homescreen_card_body_result_available">"JeÅ›li masz pozytywny wynik testu na obecność koronawirusa, możesz ostrzec innych."</string> + <!-- XTXT: homescreen card: body - error --> + <string name="ag_homescreen_card_body_error">"Ustalenie wyniku Twojego testu byÅ‚o niemożliwe."</string> + <!-- XTXT: homescreen card: body - not valid test --> + <string name="ag_homescreen_card_body_not_valid_test">"Twój test ma wiÄ™cej niż 21 dni i straciÅ‚ ważność. UsuÅ„ test. BÄ™dziesz mieć wówczas możliwość dodania kolejnego. "</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_body_result_negative">"Nie zdiagnozowano u Ciebie wirusa SARS-CoV-2."</string> + <!-- XTXT: homescreen card: body - positive --> + <string name="ag_homescreen_card_body_result_positive">"Zdiagnozowano u Ciebie wirusa SARS-CoV-2."</string> + + <!-- #################################### + Homescreen cards - rapid test + ###################################### --> + <!-- XHED: rapid test homescreen card: title --> + <string name="ag_homescreen_card_rapidtest_title">"Szybki test"</string> + <!-- XBUT: rapid test homescreen card: dont show anymore button --> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Nie wyÅ›wietlaj ponownie"</string> + <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Test nie jest już aktualny"</string> + <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Wykonaj test PCR, aby zweryfikować wynik tego testu."</string> + + <!-- Body --> + <!-- XTXT: rapid test homescreen card: body - no result --> + <string name="ag_homescreen_card_rapidtest_body_no_result">"Ustalanie wyniku Twojego szybkiego testu jeszcze siÄ™ nie zakoÅ„czyÅ‚o."</string> + <!-- XTXT: rapid test homescreen card: body - outdated test --> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Twój szybki test ma wiÄ™cej niż 48 godzin i nie bÄ™dzie już tutaj wyÅ›wietlany."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_rapid_body_result_date">"Wykonany dnia %1$s"</string> + + <!-- #################################### + Homescreen cards - PCR + ###################################### --> + <!-- XHED: PCR homescreen card: title --> + <string name="ag_homescreen_card_pcr_title">"Test PCR"</string> + <!-- XBUT: PCR homescreen card: clear test button --> + <string name="ag_homescreen_card_pcr_clear_test_button">"USUŃ TEST"</string> + + <!-- Body --> + <!-- XTXT: PCR homescreen card: body - no result --> + <string name="ag_homescreen_card_pcr_body_no_result">"Ustalanie wyniku Twojego testu PCR jeszcze siÄ™ nie zakoÅ„czyÅ‚o."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_pcr_body_result_date">"Test zarejestrowany dnia %1$s"</string> + + <!-- XBUT: submission deletion warning button continue --> + <string name="submission_deletion_warning_continue_button">"Kontynuuj"</string> + <!-- XBUT: submission deletion warning button cancel --> + <string name="submission_deletion_warning_cancel_button">"Anuluj"</string> + <!-- XHED: submission deletion warning title --> + <string name="submission_deletion_warning_title">"Uwaga"</string> + <!-- YTXT: Headline for rapid test submission deletion warning --> + <string name="submission_deletion_warning_headline_antigen_test">"Zarejestrowano już szybki test."</string> + <!-- YTXT: Body for rapid test submission deletion warning --> + <string name="submission_deletion_warning_body_antigen_test">"Zarejestrowano już szybki test. Aplikacja może zarzÄ…dzać maksymalnie jednym szybkim testem i jednym testem PCR jednoczeÅ›nie. JeÅ›li zarejestrujesz kolejny szybki test, pierwszy szybki test zostanie usuniÄ™ty z aplikacji."</string> + <!-- YTXT: Headline for PCR test submission deletion warning --> + <string name="submission_deletion_warning_headline_pcr_test">"Zarejestrowano już test PCR."</string> + <!-- YTXT: Body for PCR test submission deletion warning --> + <string name="submission_deletion_warning_body_pcr_test">"Zarejestrowano już test PCR. Aplikacja może zarzÄ…dzać maksymalnie jednym szybkim testem i jednym testem PCR jednoczeÅ›nie. JeÅ›li zarejestrujesz kolejny test PCR, pierwszy test PCR zostanie usuniÄ™ty z aplikacji."</string> + + <!-- XHED: submission result antigen fragment toolbar text --> + <string name="submission_test_result_toolbar_text">"Twój wynik testu"</string> + <!-- XHED: submission result antigen fragment card title --> + <string name="submission_test_result_antigen_title">"Szybki test"</string> + <!-- XTXT: submission result antigen fragment card "found" text --> + <string name="submission_result_subtitle">"Wyniki"</string> + <!-- XTXT: submission test result fragment negative diagnosis --> + <string name="submission_test_result_negative">"Brak zakażenia"</string> + <!-- XTXT: submission test result fragment positive diagnosis --> + <string name="submission_test_result_positive">"Zakażenie"</string> + <!-- XTXT: submission result antigen fragment card patient name placeholder --> + <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> + <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"Data urodzenia %1$s"</string> + <!-- XTXT: coronatest negative antigen result time and date placeholder --> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Wydano: %1$s, %2$s"</string> + <!-- XTXT: submission result antigen fragment card negative result message --> + <string name="submission_test_result_negative_message">"Nie zdiagnozowano u Ciebie wirusa SARS-CoV-2."</string> + <!-- XHED: submission result antigen fragment negative result proof title --> + <string name="submission_test_result_antigen_negative_proof_title">"Funkcja weryfikacji"</string> + <!-- XHED: submission result antigen fragment negative result proof body --> + <string name="submission_test_result_antigen_negative_proof_body">"WyÅ›wietlony tutaj wynik testu jest prywatny, ale niektóre przepisy prawne mogÄ… wymagać jego udostÄ™pnienia. JeÅ›li to wymagane, możesz go udostÄ™pnić na jeden z podanych sposobów."</string> + <!-- XHED: submission result antigen negative result counter title --> + <string name="submission_test_result_antigen_negative_counter_title">"Wynik dostÄ™pny od"</string> + <!-- XHED: coronatest negative antigen result first info title --> + <string name="coronatest_negative_antigen_result_first_info_title">"Twój szybki test zostaÅ‚ dodany."</string> + <!-- XTXT: coronatest negative antigen result first info body --> + <string name="coronatest_negative_antigen_result_first_info_body">"Wynik testu jest wyÅ›wietlany tutaj przez 48 godzin."</string> + <!-- XHED: coronatest negative antigen result second info title --> + <string name="coronatest_negative_antigen_result_second_info_title">"Diagnoza: brak zakażenia"</string> + <!-- XTXT: coronatest negative antigen result second info body --> + <string name="coronatest_negative_antigen_result_second_info_body">"Szybki test nie dostarczyÅ‚ dowodów na to, że masz koronawirusa SARS-CoV-2."</string> + <!-- XTXT: coronatest negative antigen result third info title --> + <string name="coronatest_negative_antigen_result_third_info_title">"UsuÅ„ test"</string> + <!-- XTXT: coronatest negative antigen result third info body --> + <string name="coronatest_negative_antigen_restul_third_info_body">"UsuÅ„ test z aplikacji Corona-Warn-App, aby w razie potrzeby można byÅ‚o zapisać w niej kod nowego testu."</string> + + <!-- #################################### + Rapid Antigen Test Profile + ###################################### --> + <!-- XHED: Create RAT profile card title --> + <string name="rat_profile_create_card_title">"Utwórz profil szybkiego testu"</string> + <!-- XTXT: Create RAT profile card subtitle --> + <string name="rat_profile_create_card_subtitle">"Możesz utworzyć profil ze swoimi danymi osobowymi, który bÄ™dziesz przesyÅ‚ać za pomocÄ… kodu QR podczas wykonywania każdego szybkiego testu."</string> + <!-- XHED: Open RAT profile card title --> + <string name="rat_profile_open_card_title">"Profil szybkiego testu"</string> + <!-- XTXT: Open RAT profile card subtitle --> + <string name="rat_profile_open_card_subtitle">"ZaoszczÄ™dź czas i przedstaw swój profil szybkiego testu w punkcie testowania."</string> + <!-- XHED: Create RAT profile title --> + <string name="rat_profile_create_title">"Profil szybkiego testu"</string> + <!-- XTXT: Create RAT profile first name hint --> + <string name="rat_profile_create_first_name_hint">"ImiÄ™"</string> + <!-- XTXT: Create RAT profile last name hint --> + <string name="rat_profile_create_last_name_hint">"Nazwisko"</string> + <!-- XTXT: Create RAT profile birth date hint --> + <string name="rat_profile_create_birth_date_hint">"Data urodzenia"</string> + <!-- XBUT: Create RAT profile save button --> + <string name="rat_profile_create_button">"Zapisz"</string> + <!-- XTXT: Create RAT profile description --> + <string name="rat_profile_create_description">"Zapisz swoje dane osobowe w postaci kodu QR, aby przyspieszyć rejestracjÄ™ w punktach testowania."</string> + <!-- XTXT: Create RAT profile street hint --> + <string name="rat_profile_create_street_hint">"Ulica i numer domu"</string> + <!-- XTXT: Create RAT profile post code hint --> + <string name="rat_profile_create_zip_code_hint">"Kod pocztowy"</string> + <!-- XTXT: Create RAT profile city hint --> + <string name="rat_profile_create_city_hint">"Miasto"</string> + <!-- XTXT: Create RAT profile phone hint --> + <string name="rat_profile_create_phone_hint">"Numer telefonu"</string> + <!-- XTXT: Create RAT profile email hint --> + <string name="rat_profile_create_email_hint">"Adres e-mail"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"Przed budynkiem stoi kobieta ze smartfonem w dÅ‚oni. Kod QR symbolizuje profil szybkiego testu do zeskanowania."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"Zapisz swoje dane osobowe w postaci kodu QR, aby przyspieszyć rejestracjÄ™ w punktach testowania."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"DziÄ™ki utworzeniu profilu szybkiego testu, nie bÄ™dziesz musiaÅ‚(a) ponownie wypeÅ‚niać swoich danych osobowych przed wykonaniem szybkiego testu."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Kontynuuj"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"Szczegółowe informacje na temat przetwarzania danych i Twojej zgodzie"</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"Pokaż ten kodu QR w punkcie testowania, aby przyÅ›pieszyć rejestracjÄ™ danych osobowych. Miej też pod rÄ™kÄ… swój dowód osobisty."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"Data urodzenia %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Kontynuuj"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"UsuÅ„"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"Czy chcesz usunąć profil szybkiego testu?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"PamiÄ™taj, że jeÅ›li to zrobisz, ten kod QR nie bÄ™dzie mógÅ‚ być już używany do rejestracji."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"UsuÅ„"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Anuluj"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml index 6d3049b2471693a9df6a04849b9908dff5380c34..852a7b72edfe78f8c7bee51ea27b859426410336 100644 --- a/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml @@ -16,34 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Rejestracja szybkich testów antygenowych"</item> - <item>"Przedstawienie dowodu"</item> - <item>"Infolinia techniczna już dostÄ™pna poza granicami Niemiec"</item> - <item>"Infolinia TAN już dostÄ™pna poza granicami Niemiec"</item> + <item>"Rejestruj dzienniki rozwiÄ…zywania problemów"</item> + <item>"Utwórz profile szybkich testów"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Oprócz wyników testów PCR, w aplikacji można teraz wyÅ›wietlać również wyniki szybkich testów antygenowych i wykorzystywać je do ostrzegania innych."</item> - <item>"JeÅ›li chcesz, możesz użyć aplikacji, aby udowodnić swój osobisty status dotyczÄ…cy zakażenia (na przykÅ‚ad negatywny wynik szybkiego testu). PamiÄ™taj jednak, że nie masz obowiÄ…zku korzystania z aplikacji w celu potwierdzenia swojego statusu dotyczÄ…cego zakażenia. Możesz również udowodnić swój status dotyczÄ…cy zakażenia w inny sposób, zgodny z przepisami prawnymi obowiÄ…zujÄ…cymi w Twoim miejscu zamieszkania."</item> - <item>"Z infoliniÄ… technicznÄ… można siÄ™ teraz poÅ‚Ä…czyć również poza terytorium Niemiec, pod numerem +49 30 498 75401. W takim przypadku obowiÄ…zujÄ… opÅ‚aty pobierane przez operatora telefonicznego."</item> - <item>"JeÅ›li zdiagnozowano u Ciebie koronawirusa, możesz teraz poprosić o numer TAN poza terytorium Niemiec, dzwoniÄ…c na numer +49 30 498 75402. W takim przypadku obowiÄ…zujÄ… opÅ‚aty pobierane przez operatora telefonicznego."</item> + <item>"Możesz teraz wygenerować dziennik bÅ‚Ä™dów w funkcji pomocy technicznej dla aplikacji. Rejestruje on kroki, które wykonujesz w aplikacji, aby uproÅ›cić analizÄ™ potencjalnych bÅ‚Ä™dów i pomóc nam szybciej je poprawić."</item> + <item>"Możesz teraz utworzyć profil szybkiego testu w swoich danych osobowych. Twój profil bÄ™dzie można później w szybki i Å‚atwy sposób zeskanować w punktach testowania za pomocÄ… kodu QR."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> <item/> - <item/> - <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> <item/> - <item/> - <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/strings.xml b/Corona-Warn-App/src/main/res/values-pl/strings.xml index dfa08080011ccd3433acf3ead09ff32fb70900c4..e4a4334d060728003f821e7aeec8e955473cd470 100644 --- a/Corona-Warn-App/src/main/res/values-pl/strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/strings.xml @@ -64,6 +64,10 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"Zainstalowano %s dni temu"</string> + <!-- XTXT: risk card - Days since installation if today --> + <string name="risk_card_body_installation_today">"Zainstalowano dzisiaj"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"Zainstalowano wczoraj"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"Rejestrowanie narażenia byÅ‚o aktywne przez %1$s z ostatnich 14 dni"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -849,7 +853,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"Aby pomóc zespoÅ‚owi pomocy technicznej aplikacji w analizie bÅ‚Ä™dów, możesz zarejestrować raport o bÅ‚Ä™dzie z CWA. Gdy to zrobisz, poszczególne kroki techniczne i wyniki procesów aplikacji zostanÄ… zarejestrowane. NastÄ™pnie możesz wysÅ‚ać raport o bÅ‚Ä™dzie do pomocy technicznej i pomóc w zidentyfikowaniu i usuniÄ™ciu bÅ‚Ä™du."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"WiÄ™cej informacji znajduje siÄ™ na naszej stronie „CzÄ™sto zadawane pytaniaâ€:\nCzÄ™sto zadawane pytania dotyczÄ…ce raportów o bÅ‚Ä™dach"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"WiÄ™cej informacji znajduje siÄ™ na naszej stronie „CzÄ™sto zadawane pytaniaâ€: CzÄ™sto zadawane pytania dotyczÄ…ce raportów o bÅ‚Ä™dach"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"CzÄ™sto zadawane pytania dotyczÄ…ce raportów o bÅ‚Ä™dach"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -864,10 +868,10 @@ <string name="debugging_debuglog_action_start_recording">"Uruchom"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Zatrzymaj i usuÅ„"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"WyÅ›lij raport o bÅ‚Ä™dzie"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Zapisz lokalnie i kontynuuj"</string> + <string name="debugging_debuglog_action_local_log_export">"UdostÄ™pnij i kontynuuj"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"Nagrywanie aktywne"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -894,6 +898,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Kontynuuj analizÄ™"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"Aby rozpocząć analizÄ™ bÅ‚Ä™du, potrzebujesz co najmniej 200 MB pamiÄ™ci. Zwolnij pamięć."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"Szczegółowe informacje na temat wysyÅ‚ania raportów o bÅ‚Ä™dach"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1041,6 +1046,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"Osoba trzyma smartfon. Kod QR w teÅ›cie symbolizuje kod do zeskanowania."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Twoja zgoda jest dobrowolna."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"Możesz pobrać swój wynik testu, nawet jeÅ›li nie zamierzasz go udostÄ™pniać. JeÅ›li jednak zdecydujesz siÄ™ na jego udostÄ™pnienie, pomożesz chronić innych przed zakażeniem."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Twoja tożsamość nie zostanie ujawniona. Inni użytkownicy nie dowiedzÄ… siÄ™, kto udostÄ™pniÅ‚ swoje wyniki testów."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"W sekcji „Moje zameldowania†znajdujÄ… siÄ™ wydarzenia i miejsca, których zameldowani goÅ›cie zostanÄ… ostrzeżeni. Możesz usunąć pojedyncze zameldowania z listy, aby wykluczyć je z procesu ostrzegania."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"Aby wyrazić zgodÄ™, musisz mieć ukoÅ„czone 16 lat."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Wynik testu"</string> @@ -1169,10 +1185,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Ze wzglÄ™du na pozytywny wynik testu na obecność koronawirusa możesz teraz ostrzegać innych za poÅ›rednictwem aplikacji."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"Funkcja ostrzegania dziaÅ‚a w kilku krajach. Obecnie sÄ… to nastÄ™pujÄ…ce kraje:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Ponieważ zdiagnozowano u Ciebie koronawirusa, możesz ostrzec innych za poÅ›rednictwem aplikacji. Jeżeli wykonaÅ‚eÅ›(-aÅ›) szybki test, funkcja ostrzegania dziaÅ‚a tylko w Niemczech. JeÅ›li wykonaÅ‚eÅ›(-aÅ›) test PCR, funkcja ostrzegania dziaÅ‚a w nastÄ™pujÄ…cych krajach:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1180,7 +1193,7 @@ <!-- XTXT: First bullet point when test result is positive and consent is requested for key sharing--> <string name="submission_test_result_positive_no_consent_text_1"><b>"UdostÄ™pnij swój wynik testu i pomóż chronić innych przed infekcjÄ…."</b></string> <!-- XTXT: Second bullet point when test result is positive and consent is requested for key sharing--> - <string name="submission_test_result_positive_no_consent_text_2">"Twoja tożsamość nie zostanie ujawniona. Inni użytkownicy nie dowiedzÄ… siÄ™, kto udostÄ™pniÅ‚ swoje wyniki testów."</string> + <string name="submission_test_result_positive_no_consent_text_2">"Twoja tożsamość nie zostanie ujawniona. Inni użytkownicy nie dowiedzÄ… siÄ™, kto udostÄ™pniÅ‚ swój wynik testu."</string> <!-- XTXT: Third bullet point when test result is positive and consent is requested for key sharing--> <string name="submission_test_result_positive_no_consent_text_3">"PostÄ™puj zgodnie z poleceniami organu ds. zdrowia publicznego i zostaÅ„ w domu, aby nie zarażać innych."</string> <!-- XBUT: Button for giving consent for key sharing --> @@ -1988,9 +2001,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"NieprawidÅ‚owy kod QR"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Zeskanowany kod QR jest nieprawidÅ‚owy.\nZeskanuj prawidÅ‚owy kod QR.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Zeskanowany kod QR nie jest prawidÅ‚owy dla zameldowania obecnoÅ›ci w wydarzeniu lub miejscu. \nZeskanuj odpowiedni kod QR.\n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"OK"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Anuluj"</string> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Ostrzeżenie o niekompatybilnoÅ›ci"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Twój smartfon może tylko odbierać powiadomienia dotyczÄ…ce COVID-19 przez Bluetooth, ale nie może ich wysyÅ‚ać. Oznacza to, że możesz otrzymywać ostrzeżenia o narażeniach za poÅ›rednictwem tego interfejsu, ale nie możesz ostrzegać innych o narażeniach. Możesz wysyÅ‚ać i odbierać ostrzeżenia, które wynikajÄ… z zameldowaÅ„."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Twój smartfon nie może wysyÅ‚ać ani odbierać powiadomieÅ„ dotyczÄ…cych COVID-19 przez Bluetooth. Możesz wysyÅ‚ać i odbierać ostrzeżenia wynikajÄ…ce z zameldowaÅ„."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..52e545312fd352bfd4297f43f3f0f9261c408c34 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + + <!-- #################################### + Homescreen cards - register test card + ###################################### --> + <!-- XHED: Register test homescreen card: title --> + <string name="ag_homescreen_card_test_register_title">"ÃŽnregistrarea testului dvs."</string> + <!-- XTXT: Register test homescreen card: body --> + <string name="ag_homescreen_card_test_register_body">"UtilizaÈ›i aplicaÈ›ia pentru a afla rezultatele testului dvs. pentru a-i putea avertiza pe ceilalÈ›i mai rapid."</string> + + <!-- #################################### + Homescreen cards - common buttons + ###################################### --> + <!-- XBUT: Homescreen card: show test button --> + <string name="ag_homescreen_card_show_button">"AfiÈ™are test"</string> + <!-- XBUT: Homescreen card: warn others button --> + <string name="ag_homescreen_card_warn_others_button">"AvertizaÈ›i-i pe ceilalÈ›i"</string> + + <!-- #################################### + Homescreen cards - common status + ###################################### --> + <!-- XTXT: Homescreen card: status subtitle - no result --> + <string name="ag_homescreen_card_status_no_result">"Rezultatul dvs. nu este încă disponibil"</string> + <!-- XTXT: Homescreen card: status subtitle - result available --> + <string name="ag_homescreen_card_status_result_available">"AflaÈ›i rezultatul testului"</string> + <!-- XTXT: Homescreen card: status subtitle - error --> + <string name="ag_homescreen_card_status_error">"Test nevalid"</string> + <!-- XTXT: Homescreen card: status subtitle - invalid --> + <string name="ag_homescreen_card_status_invalid">"Nu mai este valid"</string> + <!-- XTXT: Homescreen card: status subtitle - findings --> + <string name="ag_homescreen_card_status_findings">"Rezultat"</string> + <!-- XTXT: Homescreen card: subtitle - corona official name --> + <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> + <!-- XTXT: Homescreen card: status - negative --> + <string name="ag_homescreen_card_status_negative">"Negativ"</string> + <!-- XTXT: Homescreen card: status - positiv --> + <string name="ag_homescreen_card_status_positiv">"Pozitiv"</string> + + <!-- #################################### + Homescreen cards - common body + ###################################### --> + <!-- XTXT: homescreen card: body - result available --> + <string name="ag_homescreen_card_body_result_available">"Dacă aÈ›i fost testat pozitiv la coronavirus, îi puteÈ›i avertiza pe ceilalÈ›i."</string> + <!-- XTXT: homescreen card: body - error --> + <string name="ag_homescreen_card_body_error">"Testul dvs. nu a putut fi evaluat."</string> + <!-- XTXT: homescreen card: body - not valid test --> + <string name="ag_homescreen_card_body_not_valid_test">"Testul dvs. are o vechime de peste 21 de zile È™i, prin urmare, nu mai este relevant. Vă rugăm să È™tergeÈ›i testul. Apoi puteÈ›i adăuga altul. "</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_body_result_negative">"Nu aÈ›i fost diagnosticat cu virusul SARS-CoV-2."</string> + <!-- XTXT: homescreen card: body - positive --> + <string name="ag_homescreen_card_body_result_positive">"AÈ›i fost diagnosticat cu virusul SARS-CoV-2."</string> + + <!-- #################################### + Homescreen cards - rapid test + ###################################### --> + <!-- XHED: rapid test homescreen card: title --> + <string name="ag_homescreen_card_rapidtest_title">"Test rapid"</string> + <!-- XBUT: rapid test homescreen card: dont show anymore button --> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Nu mai afiÈ™a din nou"</string> + <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Testul nu mai este de actualitate"</string> + <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"EfectuaÈ›i un test PCR pentru a verifica rezultatul acestui test."</string> + + <!-- Body --> + <!-- XTXT: rapid test homescreen card: body - no result --> + <string name="ag_homescreen_card_rapidtest_body_no_result">"Evaluarea testului dvs. rapid nu este încă finalizată."</string> + <!-- XTXT: rapid test homescreen card: body - outdated test --> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Testul dvs. rapid are peste 48 de ore È™i nu va mai fi afiÈ™at aici."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_rapid_body_result_date">"Efectuat pe %1$s"</string> + + <!-- #################################### + Homescreen cards - PCR + ###################################### --> + <!-- XHED: PCR homescreen card: title --> + <string name="ag_homescreen_card_pcr_title">"Test PCR"</string> + <!-- XBUT: PCR homescreen card: clear test button --> + <string name="ag_homescreen_card_pcr_clear_test_button">"ȘTERGERE TEST"</string> + + <!-- Body --> + <!-- XTXT: PCR homescreen card: body - no result --> + <string name="ag_homescreen_card_pcr_body_no_result">"Evaluarea testului dvs. PCR nu este încă finalizată."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_pcr_body_result_date">"Test înregistrat pe %1$s"</string> + + <!-- XBUT: submission deletion warning button continue --> + <string name="submission_deletion_warning_continue_button">"Continuare"</string> + <!-- XBUT: submission deletion warning button cancel --> + <string name="submission_deletion_warning_cancel_button">"Anulare"</string> + <!-- XHED: submission deletion warning title --> + <string name="submission_deletion_warning_title">"Notă"</string> + <!-- YTXT: Headline for rapid test submission deletion warning --> + <string name="submission_deletion_warning_headline_antigen_test">"AÈ›i înregistrat deja un test rapid."</string> + <!-- YTXT: Body for rapid test submission deletion warning --> + <string name="submission_deletion_warning_body_antigen_test">"AÈ›i înregistrat deja un test rapid. AplicaÈ›ia poate gestiona cel mult un test rapid È™i un test PCR în acelaÈ™i timp. Dacă înregistraÈ›i un alt test rapid, primul test rapid va fi È™ters din aplicaÈ›ie."</string> + <!-- YTXT: Headline for PCR test submission deletion warning --> + <string name="submission_deletion_warning_headline_pcr_test">"AÈ›i înregistrat deja un test PCR."</string> + <!-- YTXT: Body for PCR test submission deletion warning --> + <string name="submission_deletion_warning_body_pcr_test">"AÈ›i înregistrat deja un test PCR. AplicaÈ›ia poate gestiona cel mult un test rapid È™i un test PCR în acelaÈ™i timp. Dacă înregistraÈ›i un alt test PCR, primul test PCR va fi È™ters din aplicaÈ›ie."</string> + + <!-- XHED: submission result antigen fragment toolbar text --> + <string name="submission_test_result_toolbar_text">"Rezultatul testului dvs."</string> + <!-- XHED: submission result antigen fragment card title --> + <string name="submission_test_result_antigen_title">"Test rapid"</string> + <!-- XTXT: submission result antigen fragment card "found" text --> + <string name="submission_result_subtitle">"Rezultat"</string> + <!-- XTXT: submission test result fragment negative diagnosis --> + <string name="submission_test_result_negative">"Negativ"</string> + <!-- XTXT: submission test result fragment positive diagnosis --> + <string name="submission_test_result_positive">"Pozitiv"</string> + <!-- XTXT: submission result antigen fragment card patient name placeholder --> + <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> + <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"Născut(ă) pe %1$s"</string> + <!-- XTXT: coronatest negative antigen result time and date placeholder --> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Emis la: %1$s, %2$s"</string> + <!-- XTXT: submission result antigen fragment card negative result message --> + <string name="submission_test_result_negative_message">"Nu aÈ›i fost diagnosticat cu virusul SARS-CoV-2."</string> + <!-- XHED: submission result antigen fragment negative result proof title --> + <string name="submission_test_result_antigen_negative_proof_title">"Caracteristica de verificare"</string> + <!-- XHED: submission result antigen fragment negative result proof body --> + <string name="submission_test_result_antigen_negative_proof_body">"Rezultatul testului dvs. este privat, deÈ™i unele legi vă pot solicita să îl partajaÈ›i. Când vi se solicită acest lucru, îl puteÈ›i arăta prin intermediul unuia dintre modurile furnizate."</string> + <!-- XHED: submission result antigen negative result counter title --> + <string name="submission_test_result_antigen_negative_counter_title">"Rezultat disponibil de la"</string> + <!-- XHED: coronatest negative antigen result first info title --> + <string name="coronatest_negative_antigen_result_first_info_title">"Testul dvs. rapid a fost adăugat."</string> + <!-- XTXT: coronatest negative antigen result first info body --> + <string name="coronatest_negative_antigen_result_first_info_body">"Rezultatul testului dvs. este afiÈ™at timp de 48 de ore."</string> + <!-- XHED: coronatest negative antigen result second info title --> + <string name="coronatest_negative_antigen_result_second_info_title">"Diagnostic negativ"</string> + <!-- XTXT: coronatest negative antigen result second info body --> + <string name="coronatest_negative_antigen_result_second_info_body">"Testul rapid furnizat nu a făcut dovada că aveÈ›i coronavirusul SARS-CoV-2."</string> + <!-- XTXT: coronatest negative antigen result third info title --> + <string name="coronatest_negative_antigen_result_third_info_title">"Eliminare test"</string> + <!-- XTXT: coronatest negative antigen result third info body --> + <string name="coronatest_negative_antigen_restul_third_info_body">"ȘtergeÈ›i testul din Corona-Warn-App pentru a salva un nou cod de test aici dacă este necesar."</string> + + <!-- #################################### + Rapid Antigen Test Profile + ###################################### --> + <!-- XHED: Create RAT profile card title --> + <string name="rat_profile_create_card_title">"Crearea profilului de test rapid"</string> + <!-- XTXT: Create RAT profile card subtitle --> + <string name="rat_profile_create_card_subtitle">"PuteÈ›i crea un profil cu datele dvs. personale, pe care îl puteÈ›i transmite prin intermediul codului QR la fiecare test rapid."</string> + <!-- XHED: Open RAT profile card title --> + <string name="rat_profile_open_card_title">"Profil de test rapid"</string> + <!-- XTXT: Open RAT profile card subtitle --> + <string name="rat_profile_open_card_subtitle">"EconomisiÈ›i timp È™i prezentaÈ›i profilul dvs. de test rapid la punctul de testare."</string> + <!-- XHED: Create RAT profile title --> + <string name="rat_profile_create_title">"Profil de test rapid"</string> + <!-- XTXT: Create RAT profile first name hint --> + <string name="rat_profile_create_first_name_hint">"Prenume"</string> + <!-- XTXT: Create RAT profile last name hint --> + <string name="rat_profile_create_last_name_hint">"Nume"</string> + <!-- XTXT: Create RAT profile birth date hint --> + <string name="rat_profile_create_birth_date_hint">"Data naÈ™terii"</string> + <!-- XBUT: Create RAT profile save button --> + <string name="rat_profile_create_button">"Salvare"</string> + <!-- XTXT: Create RAT profile description --> + <string name="rat_profile_create_description">"SalvaÈ›i datele dvs. personale sub formă de cod QR pentru a grăbi înregistrarea în punctele de testare."</string> + <!-- XTXT: Create RAT profile street hint --> + <string name="rat_profile_create_street_hint">"Strada È™i numărul"</string> + <!-- XTXT: Create RAT profile post code hint --> + <string name="rat_profile_create_zip_code_hint">"Cod poÈ™tal"</string> + <!-- XTXT: Create RAT profile city hint --> + <string name="rat_profile_create_city_hint">"Localitate"</string> + <!-- XTXT: Create RAT profile phone hint --> + <string name="rat_profile_create_phone_hint">"Număr telefon"</string> + <!-- XTXT: Create RAT profile email hint --> + <string name="rat_profile_create_email_hint">"Adresă e-mail"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"O femeie cu un smartphone în mână stă în faÈ›a unei clădiri. Un cod QR simbolizează profilul de test rapid de scanat."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"SalvaÈ›i datele dvs. personale sub formă de cod QR pentru a grăbi înregistrarea în punctele de testare."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"Când creaÈ›i un profil de test rapid nu va trebui să completaÈ›i din nou datele dvs. înainte de fiecare test rapid."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Continuare"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"InformaÈ›ii detaliate privind Prelucrarea datelor È™i Consimțământul dvs."</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"PrezentaÈ›i acest cod QR în punctul de testare pentru ca datele dvs. personale să poată fi înregistrate mai repede. De asemenea, È›ineÈ›i cartea de identitate la îndemână."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"Născut(ă) pe %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Continuare"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"Eliminare"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"DoriÈ›i să eliminaÈ›i profilul de test rapid?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"LuaÈ›i în considerare că dacă îl È™tergeÈ›i, codul QR nu mai poate fi utilizat pentru înregistrare."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"Ștergere"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Anulare"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml index 7f09cfd654d8d3d4112af8946f2723c2d3493d84..7515e68be77321299607746518d64501711f990b 100644 --- a/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml @@ -16,34 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"ÃŽnregistrarea testelor antigen rapide"</item> - <item>"AfiÈ™area dovezii"</item> - <item>"Hotline tehnic disponibil acum din afara Germaniei"</item> - <item>"Hotline TAN disponibil acum din afara Germaniei"</item> + <item>"ÃŽnregistrarea jurnalelor de depanare"</item> + <item>"Crearea profilurilor de test rapid"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Pe lângă rezultatele testelor PCR, acum puteÈ›i afiÈ™a È™i rezultatele testelor antigen rapide în aplicaÈ›ie È™i le puteÈ›i utiliza pentru a-i avertiza pe ceilalÈ›i."</item> - <item>"Dacă doriÈ›i, puteÈ›i utiliza aplicaÈ›ia pentru a dovedi starea de infectare personală (de exemplu, un test rapid negativ). ReÈ›ineÈ›i, totuÈ™i, că nu aveÈ›i nicio obligaÈ›ie de a utiliza aplicaÈ›ia pentru a dovedi starea dvs. de infectare. De asemenea, puteÈ›i dovedi starea dvs. de infectare într-un alt mod care respectă reglementările legale din localitatea dvs. de domiciliu."</item> - <item>"Acum puteÈ›i contacta hotline-ul tehnic È™i din afara Germaniei, la numărul +49 30 498 75401. Se vor aplica tarifele furnizorului dvs. de servicii de telefonie."</item> - <item>"Dacă sunteÈ›i diagnosticat cu coronavirus, acum puteÈ›i solicita un cod TAN din afara Germaniei, la numărul +49 30 498 75402. Se vor aplica tarifele furnizorului dvs. de servicii de telefonie."</item> + <item>"Acum puteÈ›i genera un jurnal de erori în caracteristica de suport tehnic pentru aplicaÈ›ie. Aceasta înregistrează etapele pe care le efectuaÈ›i în aplicaÈ›ie, pentru a simplifica analiza de erori potenÈ›iale È™i pentru a ne ajuta să le corectăm mai rapid."</item> + <item>"Acum puteÈ›i crea un profil de test rapid în datele dvs. personale. Apoi, profilul dvs. poate fi scanat în punctele de testare, rapid È™i uÈ™or, prin intermediul codului QR."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> <item/> - <item/> - <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> <item/> - <item/> - <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/strings.xml b/Corona-Warn-App/src/main/res/values-ro/strings.xml index bc5edb78491b079299ea5ec7b3c83c35e93a9864..09d09ae87e87c3b1518df057bb1bb0df0059d149 100644 --- a/Corona-Warn-App/src/main/res/values-ro/strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/strings.xml @@ -64,6 +64,10 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"Instalat acum %s zile"</string> + <!-- XTXT: risk card - Days since installation if today --> + <string name="risk_card_body_installation_today">"Instalat astăzi"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"Instalat ieri"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"ÃŽn ultimele 14 zile, înregistrarea în jurnal a expunerilor a fost activă timp de %1$s zile"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -849,7 +853,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"Pentru a ajuta echipa de suport tehnic a aplicaÈ›iei la analiza erorilor, puteÈ›i înregistra un raport de erori din aplicaÈ›ia Corona-Warn. Astfel, sunt înregistrate etapele tehnice individuale È™i rezultatele proceselor aplicaÈ›iei. PuteÈ›i trimite apoi raportul de erori la suportul tehnic È™i ajutaÈ›i la identificarea È™i corectarea erorilor."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"Pentru mai multe informaÈ›ii, consultaÈ›i pagina noastră de întrebări frecvente:\nÃŽntrebări frecvente despre rapoartele de erori"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"Pentru mai multe informaÈ›ii, consultaÈ›i pagina noastră de întrebări frecvente: ÃŽntrebări frecvente despre rapoartele de erori"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"ÃŽntrebări frecvente despre rapoartele de erori"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -864,10 +868,10 @@ <string name="debugging_debuglog_action_start_recording">"Pornire"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Oprire È™i È™tergere"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"Trimitere raport de erori"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Salvare locală È™i continuare"</string> + <string name="debugging_debuglog_action_local_log_export">"Partajare È™i continuare"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"ÃŽnregistrare activă"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -894,6 +898,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Continuare analiză"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"AveÈ›i nevoie de cel puÈ›in 200 MB de memorie pentru a începe analiza erorilor. EliberaÈ›i memorie."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"InformaÈ›ii detaliate despre trimiterea rapoartelor de erori"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1041,6 +1046,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"O persoană È›ine în mână un smartphone. Un cod QR de pe un test simbolizează codul care va fi scanat."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Consimțământul dvs. este voluntar."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"PuteÈ›i afla rezultatul testului dvs. chiar dacă alegeÈ›i să nu îl partajaÈ›i. Dar dacă partajaÈ›i rezultatul testului dvs., veÈ›i ajuta la protejarea celorlalÈ›i împotriva infectării."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Identitatea dvs. va rămâne secretă. AlÈ›i utilizatori nu vor afla cine a partajat rezultatul testului."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"La „Check-inurile meleâ€, puteÈ›i vedea evenimentele È™i locurile ale căror invitaÈ›i cu check-inul făcut vor fi avertizaÈ›i. PuteÈ›i elimina check-inuri individuale din listă pentru a le exclude din procesul de avertizare."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"Trebuie să aveÈ›i cel puÈ›in 16 ani pentru a vă acorda consimțământul."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Rezultat test"</string> @@ -1169,10 +1185,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Deoarece aÈ›i fost testat pozitiv la coronavirus, acum puteÈ›i să îi avertizaÈ›i pe ceilalÈ›i prin intermediul aplicaÈ›iei."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"Caracteristica de avertizare funcÈ›ionează în câteva țări. Următoarele țări participă în prezent:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Deoarece aÈ›i fost diagnosticat cu coronavirus, puteÈ›i să îi avertizaÈ›i pe ceilalÈ›i prin intermediul aplicaÈ›iei. Dacă aÈ›i făcut un test rapid, caracteristica de avertizare funcÈ›ionează doar în Germania. Dacă aÈ›i făcut un test PCR, caracteristica de avertizare funcÈ›ionează doar în următoarele țări:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1988,9 +2001,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"Cod QR nevalabil"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Codul QR scanat nu este valabil.\nScanaÈ›i un cod QR valabil.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Codul QR scanat nu este valabil pentru check-inurile la un eveniment sau într-un loc. \nScanaÈ›i un cod QR corespunzător.\n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"OK"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Anulare"</string> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Avertizare de incompatibilitate"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Smartphone-ul dvs. poate doar primi notificări despre COVID-19 prin Bluetooth, dar nu le poate expedia. Aceasta înseamnă că puteÈ›i fi avertizat privind expunerile prin această interfață, dar dvs. nu îi puteÈ›i avertiza pe ceilalÈ›i de expuneri. PuteÈ›i trimite È™i primi avertizări care rezultă din check-inuri."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Smartphone-ul dvs. nu poate trimite sau primi notificări despre COVID-19 prin Bluetooth. PuteÈ›i trimite È™i primi avertizări care rezultă din check-inuri."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..68a20d21463aefdba94be03d5df062e87c619313 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + + <!-- #################################### + Homescreen cards - register test card + ###################################### --> + <!-- XHED: Register test homescreen card: title --> + <string name="ag_homescreen_card_test_register_title">"Testinizi Kaydedin"</string> + <!-- XTXT: Register test homescreen card: body --> + <string name="ag_homescreen_card_test_register_body">"DiÄŸer kullanıcıları daha hızlı uyarmak için test sonuçlarınızı almak üzere uygulamayı kullanın."</string> + + <!-- #################################### + Homescreen cards - common buttons + ###################################### --> + <!-- XBUT: Homescreen card: show test button --> + <string name="ag_homescreen_card_show_button">"Testi Göster"</string> + <!-- XBUT: Homescreen card: warn others button --> + <string name="ag_homescreen_card_warn_others_button">"DiÄŸer Kullanıcıları Uyarın"</string> + + <!-- #################################### + Homescreen cards - common status + ###################################### --> + <!-- XTXT: Homescreen card: status subtitle - no result --> + <string name="ag_homescreen_card_status_no_result">"Sonucunuz henüz çıkmadı"</string> + <!-- XTXT: Homescreen card: status subtitle - result available --> + <string name="ag_homescreen_card_status_result_available">"Test Sonucunu Al"</string> + <!-- XTXT: Homescreen card: status subtitle - error --> + <string name="ag_homescreen_card_status_error">"Geçersiz Test"</string> + <!-- XTXT: Homescreen card: status subtitle - invalid --> + <string name="ag_homescreen_card_status_invalid">"Artık Geçerli DeÄŸil"</string> + <!-- XTXT: Homescreen card: status subtitle - findings --> + <string name="ag_homescreen_card_status_findings">"Bulgular"</string> + <!-- XTXT: Homescreen card: subtitle - corona official name --> + <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> + <!-- XTXT: Homescreen card: status - negative --> + <string name="ag_homescreen_card_status_negative">"Negatif"</string> + <!-- XTXT: Homescreen card: status - positiv --> + <string name="ag_homescreen_card_status_positiv">"Pozitif"</string> + + <!-- #################################### + Homescreen cards - common body + ###################################### --> + <!-- XTXT: homescreen card: body - result available --> + <string name="ag_homescreen_card_body_result_available">"Koronavirüs testinizin sonucu pozitifse diÄŸer kullanıcıları uyarabilirsiniz."</string> + <!-- XTXT: homescreen card: body - error --> + <string name="ag_homescreen_card_body_error">"Testiniz deÄŸerlendirilemedi."</string> + <!-- XTXT: homescreen card: body - not valid test --> + <string name="ag_homescreen_card_body_not_valid_test">"Testiniz 21 günden eski ve bu nedenle artık geçerli deÄŸil. Lütfen testi silin. Ardından baÅŸka bir test ekleyebilirsiniz. "</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_body_result_negative">"SARS-CoV-2 virüsü tanısı almadınız."</string> + <!-- XTXT: homescreen card: body - positive --> + <string name="ag_homescreen_card_body_result_positive">"SARS-CoV-2 virüsü tanısı aldınız."</string> + + <!-- #################################### + Homescreen cards - rapid test + ###################################### --> + <!-- XHED: rapid test homescreen card: title --> + <string name="ag_homescreen_card_rapidtest_title">"Hızlı Test"</string> + <!-- XBUT: rapid test homescreen card: dont show anymore button --> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Tekrar Görüntüleme"</string> + <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Test Artık Geçerli DeÄŸil"</string> + <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Bu test sonucunu doÄŸrulamak için PCR testi yapın."</string> + + <!-- Body --> + <!-- XTXT: rapid test homescreen card: body - no result --> + <string name="ag_homescreen_card_rapidtest_body_no_result">"Hızlı testinize iliÅŸkin deÄŸerlendirme henüz tamamlanmadı."</string> + <!-- XTXT: rapid test homescreen card: body - outdated test --> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Hızlı testiniz 48 saatten eski ve artık burada görüntülenmeyecek."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_rapid_body_result_date">"%1$s tarihinde yapıldı"</string> + + <!-- #################################### + Homescreen cards - PCR + ###################################### --> + <!-- XHED: PCR homescreen card: title --> + <string name="ag_homescreen_card_pcr_title">"PCR Testi"</string> + <!-- XBUT: PCR homescreen card: clear test button --> + <string name="ag_homescreen_card_pcr_clear_test_button">"TESTÄ° SÄ°L"</string> + + <!-- Body --> + <!-- XTXT: PCR homescreen card: body - no result --> + <string name="ag_homescreen_card_pcr_body_no_result">"PCR testinize iliÅŸkin deÄŸerlendirme henüz tamamlanmadı."</string> + <!-- XTXT: homescreen card: body - negative --> + <string name="ag_homescreen_card_pcr_body_result_date">"Test %1$s tarihinde kaydedildi"</string> + + <!-- XBUT: submission deletion warning button continue --> + <string name="submission_deletion_warning_continue_button">"Devam"</string> + <!-- XBUT: submission deletion warning button cancel --> + <string name="submission_deletion_warning_cancel_button">"Ä°ptal Et"</string> + <!-- XHED: submission deletion warning title --> + <string name="submission_deletion_warning_title">"Not"</string> + <!-- YTXT: Headline for rapid test submission deletion warning --> + <string name="submission_deletion_warning_headline_antigen_test">"Zaten bir hızlı test kaydı oluÅŸturdunuz."</string> + <!-- YTXT: Body for rapid test submission deletion warning --> + <string name="submission_deletion_warning_body_antigen_test">"Zaten bir hızlı test kaydı oluÅŸturdunuz. Uygulama tek seferde en fazla bir hızlı testi ve bir PCR testini yönetebilir. BaÅŸka bir hızlı test kaydı oluÅŸturursanız ilk hızlı test uygulamadan silinir."</string> + <!-- YTXT: Headline for PCR test submission deletion warning --> + <string name="submission_deletion_warning_headline_pcr_test">"Zaten bir PCR testi kaydı oluÅŸturdunuz."</string> + <!-- YTXT: Body for PCR test submission deletion warning --> + <string name="submission_deletion_warning_body_pcr_test">"Zaten bir PCR testi kaydı oluÅŸturdunuz. Uygulama tek seferde en fazla bir hızlı testi ve bir PCR testini yönetebilir. BaÅŸka bir PCR testi kaydı oluÅŸturursanız ilk PCR testi uygulamadan silinir."</string> + + <!-- XHED: submission result antigen fragment toolbar text --> + <string name="submission_test_result_toolbar_text">"Test Sonucunuz"</string> + <!-- XHED: submission result antigen fragment card title --> + <string name="submission_test_result_antigen_title">"Hızlı Test"</string> + <!-- XTXT: submission result antigen fragment card "found" text --> + <string name="submission_result_subtitle">"Bulgular"</string> + <!-- XTXT: submission test result fragment negative diagnosis --> + <string name="submission_test_result_negative">"Negatif"</string> + <!-- XTXT: submission test result fragment positive diagnosis --> + <string name="submission_test_result_positive">"Pozitif"</string> + <!-- XTXT: submission result antigen fragment card patient name placeholder --> + <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> + <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"DoÄŸum tarihi: %1$s"</string> + <!-- XTXT: coronatest negative antigen result time and date placeholder --> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Düzenlenme: %1$s, %2$s"</string> + <!-- XTXT: submission result antigen fragment card negative result message --> + <string name="submission_test_result_negative_message">"SARS-CoV-2 virüsü tanısı almadınız."</string> + <!-- XHED: submission result antigen fragment negative result proof title --> + <string name="submission_test_result_antigen_negative_proof_title">"DoÄŸrulama ÖzelliÄŸi"</string> + <!-- XHED: submission result antigen fragment negative result proof body --> + <string name="submission_test_result_antigen_negative_proof_body">"Test sonucunuz özeldir ancak bazı yasalar nedeniyle paylaÅŸmanız gerekebilir. PaylaÅŸmanız gerekirse sunulan pek çok yöntemden birini kullanarak gösterebilirsiniz."</string> + <!-- XHED: submission result antigen negative result counter title --> + <string name="submission_test_result_antigen_negative_counter_title">"Sonucun çıktığı tarih:"</string> + <!-- XHED: coronatest negative antigen result first info title --> + <string name="coronatest_negative_antigen_result_first_info_title">"Hızlı testiniz eklendi."</string> + <!-- XTXT: coronatest negative antigen result first info body --> + <string name="coronatest_negative_antigen_result_first_info_body">"Test sonucu 48 saat süreyle burada görüntülenir."</string> + <!-- XHED: coronatest negative antigen result second info title --> + <string name="coronatest_negative_antigen_result_second_info_title">"Negatif Tanı"</string> + <!-- XTXT: coronatest negative antigen result second info body --> + <string name="coronatest_negative_antigen_result_second_info_body">"Hızlı test, koronavirüs SARS-CoV-2’ye yakalandığınıza dair hiçbir kanıt saÄŸlamadı."</string> + <!-- XTXT: coronatest negative antigen result third info title --> + <string name="coronatest_negative_antigen_result_third_info_title">"Testi Kaldır"</string> + <!-- XTXT: coronatest negative antigen result third info body --> + <string name="coronatest_negative_antigen_restul_third_info_body">"Gerekirse yeni bir test kodu kaydedebilmeniz için lütfen testi Corona-Warn-App\'ten silin."</string> + + <!-- #################################### + Rapid Antigen Test Profile + ###################################### --> + <!-- XHED: Create RAT profile card title --> + <string name="rat_profile_create_card_title">"Hızlı Test Profili OluÅŸtur"</string> + <!-- XTXT: Create RAT profile card subtitle --> + <string name="rat_profile_create_card_subtitle">"KiÅŸisel verilerinizle bir profil oluÅŸturabilir ve her hızlı test sırasında QR kod aracılığıyla gönderebilirsiniz."</string> + <!-- XHED: Open RAT profile card title --> + <string name="rat_profile_open_card_title">"Hızlı Test Profili"</string> + <!-- XTXT: Open RAT profile card subtitle --> + <string name="rat_profile_open_card_subtitle">"Zamandan tasarruf edip test noktasında hızlı test profilinizi sunabilirsiniz."</string> + <!-- XHED: Create RAT profile title --> + <string name="rat_profile_create_title">"Hızlı Test Profili"</string> + <!-- XTXT: Create RAT profile first name hint --> + <string name="rat_profile_create_first_name_hint">"Ad"</string> + <!-- XTXT: Create RAT profile last name hint --> + <string name="rat_profile_create_last_name_hint">"Soyadı"</string> + <!-- XTXT: Create RAT profile birth date hint --> + <string name="rat_profile_create_birth_date_hint">"DoÄŸum Tarihi"</string> + <!-- XBUT: Create RAT profile save button --> + <string name="rat_profile_create_button">"Kaydet"</string> + <!-- XTXT: Create RAT profile description --> + <string name="rat_profile_create_description">"Test noktalarında kaydınızı hızlandırmak için kiÅŸisel verilerinizi bir QR kod olarak kaydedin."</string> + <!-- XTXT: Create RAT profile street hint --> + <string name="rat_profile_create_street_hint">"Sokak ve Konut Numarası"</string> + <!-- XTXT: Create RAT profile post code hint --> + <string name="rat_profile_create_zip_code_hint">"Posta Kodu"</string> + <!-- XTXT: Create RAT profile city hint --> + <string name="rat_profile_create_city_hint">"Åžehir"</string> + <!-- XTXT: Create RAT profile phone hint --> + <string name="rat_profile_create_phone_hint">"Telefon Numarası"</string> + <!-- XTXT: Create RAT profile email hint --> + <string name="rat_profile_create_email_hint">"E-posta Adresi"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"Elinde akıllı telefon tutan bir kadın bir binanın önünde duruyor. QR kod, taranacak hızlı test profilini sembolize ediyor."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"Test noktalarında kaydınızı hızlandırmak için kiÅŸisel verilerinizi bir QR kod olarak kaydedin."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"Hızlı test profili oluÅŸturduÄŸunuzda her hızlı testten önce kiÅŸisel verilerinizi yeniden doldurmanız gerekmez."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Devam Et"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"Veri Ä°ÅŸleme ve Onayınız Hakkında Ayrıntılı Bilgiler"</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"KiÅŸisel verilerinizin daha hızlı kaydedilebilmesi için lütfen test noktasında bu QR kodu sunun. Kimlik kartınızı da elinizde tutun."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"DoÄŸum tarihi: %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Devam Et"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"Kaldır"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"Hızlı test profilini kaldırmak istiyor musunuz?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"Lütfen unutmayın; QR kodu silerseniz artık kayıt yaparken bunu kullanamazsınız."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"Sil"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Ä°ptal Et"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/legal_strings.xml b/Corona-Warn-App/src/main/res/values-tr/legal_strings.xml index 47cf6a2bf11559775003354bd89b3e80499bf83f..4537345aefc0db41d817d76dfa9edbff49dca3f1 100644 --- a/Corona-Warn-App/src/main/res/values-tr/legal_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/legal_strings.xml @@ -29,13 +29,15 @@ <!-- YTXT: Body for your consent screen agreement share symptoms --> <string name="submission_your_consent_agreement_share_symptoms">"<b>Hızlı test durumunda sadece Korona Uyarı Uygulamasının kullanıcıları uyarılır. Semptomlarınızın baÅŸladığına iliÅŸkin ek bilgiler verirseniz bu veriler de paylaşılır.</b>\n\nYukarıdaki “DiÄŸer Kullanıcıları Uyarın†seçeneÄŸini devre dışı bırakarak, verdiÄŸiniz rıza beyanını geri alabilirsiniz."</string> <!-- YTXT: Body for your consent screen agreement share symptoms with additional hint for test result--> - <string name="submission_your_consent_agreement_share_symptoms_2">"<b>Ayrıca semptomlarınızın baÅŸlangıcı hakkında bilgi verirseniz, bu veri de paylaşılacaktır.</b>\n\nYukarıdaki “DiÄŸer Kullanıcıları Uyarın†seçeneÄŸini devre dışı bırakarak, verdiÄŸiniz rıza beyanını geri alabilirsiniz. Bunun ardından test sonucunuz size görüntülenir.</string> + <string name="submission_your_consent_agreement_share_symptoms_2">"<b>Hızlı test durumunda sadece Korona Uyarı Uygulamasının kullanıcıları uyarılır. Semptomlarınızın baÅŸladığına iliÅŸkin ek bilgiler verirseniz bu veriler de paylaşılır.</b>\n\nYukarıdaki “DiÄŸer Kullanıcıları Uyarın†seçeneÄŸini devre dışı bırakarak, verdiÄŸiniz rıza beyanını geri alabilirsiniz. Bunun ardından test sonucunuz size görüntülenir.</string> <!-- YTXT: Body for keys submission no consent text first part--> <string name="submission_no_consent_your_consent_subsection_body_first_part">"“Kabul ediyorumâ€a tıklayarak, aÅŸağıdaki adımlara onay vermiÅŸ olursunuz:"</string> <!-- YTXT: Body for keys submission no consent text second part--> - <string name="submission_no_consent_your_consent_subsection_body_second_part">"<b>Uygulama, karşılaÅŸtığınız resmi Korona uygulamaları kullanıcıları uyarmak üzere onlara test sonucunuzu paylaşır. Uyarı, yukarıda sözü geçen ülkelerde çalışır. Ayrıca semptomlarınızın baÅŸlangıcı hakkında bilgi verirseniz, bu veri de paylaşılacaktır.</b> Lütfen bir sonraki adımda “PaylaÅŸâ€a tıklayarak test sonucunuzu (daha doÄŸrusu: Son 14 güne ait rastgele kimlik no\'larınız) paylaşın."</string> + <string name="submission_no_consent_your_consent_subsection_body_second_part">"<b>Uygulama test sonucunuzu, karşılaÅŸtığınız kullanıcıları uyarmak için paylaşır. Bu yukarıda belirtilen ülkelerdeki Korona Uygulamasının kullanıcıları ve aynı zamanda aynı etkinlikte ya da yerde bulunduÄŸunuz kullanıcılar için geçerlidir. Hızlı test durumunda sadece Korona Uyarı Uygulamasının kullanıcıları uyarılır. Semptomlarınızın baÅŸladığına iliÅŸkin ek bilgiler verirseniz bu veriler de paylaşılır.</b>"</string> <!-- YTXT: Body for keys submission no consent text third part--> - <string name="submission_no_consent_your_consent_subsection_body_third_part">"Uyarı gerçekleÅŸtikten sonra, Uygulamayı silerek verdiÄŸiniz rıza beyanını da geri alabilirsiniz."</string> + <string name="submission_no_consent_your_consent_subsection_body_third_part">"Sonraki adımada, rastgele kimiliklerinize eriÅŸim onayı verin."</string> + <!-- YTXT: Body for keys submission no consent text fourth part--> + <string name="submission_no_consent_your_consent_subsection_body_fourth_part">"Uyarı gerçekleÅŸtikten sonra, Uygulamayı silerek verdiÄŸiniz rıza beyanını da geri alabilirsiniz."</string> <!-- XTXT: Title for legal information of the contact diary onboarding screen --> <string name="contact_diary_onboarding_privacy_information_title">"Veri Ä°ÅŸlemeye Ä°liÅŸkin Notlar"</string> @@ -145,4 +147,22 @@ <string name="trace_location_onboarding_body_consent5">EÄŸer kendiniz daha sonra bir uyarıyı tetiklerseniz, “GiriÅŸ denetimlerim†altında girilen bir olay veya konumda sizinle eÅŸ zamanlı giriÅŸ yapmış olan diÄŸer tüm kullanıcılar, olay kimliÄŸi üzerinden otomatik olarak uyarılacaktır.</string> <!-- YMSG: Onboarding trace location bullet points --> <string name="trace_location_onboarding_body_consent6">“GiriÅŸ denetimlerim†altındaki bir giriÅŸ denetimini istediÄŸiniz zaman silebilirsiniz. Bu durumda akıllı telefonunuz yalnızca, bir riskli karşılaÅŸmada testi pozitif çıkan bir kullanıcının rastgele kimlik numaralarını alırsa uyarılırsınız.</string> + + <!-- RAT Profile Onboarding --> + <!-- XHED: Title for RAT Profile onboarding consent card --> + <string name="rat_profile_onboarding_card_title" translatable="false">Veri koruma ve veri güvenliÄŸi</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent1" translatable="false">Uygulamada hızlı test profillerinin oluÅŸturulması ve bunların kullanılması isteÄŸe baÄŸlıdır.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent2" translatable="false">Uygulama hızlı test profiliniz için girdiÄŸiniz verileri kiÅŸisel kare kodunuza dönüştürür. Verilen tüm bilgiler kare kodunda mevcuttur.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent3" translatable="false">Test merkezine verilerinizi hızlı ve güvenli bir ÅŸekilde hazırlamak için test merkezinde hızlı test profilinizin kare kodunu taratabilirsiniz. Test merkezi verilerinizi test uygulamak için kullanır. Verilerinizi uygulama olmadan da sunabilirsiniz. Kare kodunda bulunan ve test merkezine baÅŸka ÅŸekilde sunacağınız verileri kendiniz belirlersiniz.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent4" translatable="false">KiÅŸisel hızlı test profilinize öncelikle sadece siz eriÅŸebilirsiniz. Hızlı test profilinizin kare kodunu baÅŸkalarına gösterirseniz ancak o zaman bu kiÅŸiler hızlı test profilinizdeki kiÅŸisel verilere eriÅŸebilir. Bu sırada uygulamadaki diÄŸer verilere eriÅŸim saÄŸlanmaz (örn. Check-ins ya da rehber bilgileri gibi).</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent5" translatable="false">Hızlı test profilinizi istediÄŸiniz zaman tekrar kaldırabilirsiniz. O zamana kadar akıllı telefonunuzdaki uygulamada kayıtlı kalır.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent6" translatable="false">Kare kodu kiÅŸisel verilerinizi içerir. Verilerinizin okunmasını istemiyorsanız kiÅŸisel kare kodunuzu kimseyle paylaÅŸmayın. KiÅŸisel kare kodunuzu paylaÅŸmamanızı ve e-posta üzerinden göndermemenizi öneriyoruz.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent7" translatable="false">Özel ÅŸahıslar ya da ÅŸirketler hızlı test profilinizi göstermenizi talep edemezler.</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml index 0efde2638eecbeb771c98d74a50f6e743bbc5cdb..a8a5ef1c454ebf4d39c4366b5d04471ca6993982 100644 --- a/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml @@ -16,34 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Hızlı Antijen Testlerinin Kaydı"</item> - <item>"Kanıtın Görüntülenmesi"</item> - <item>"Teknik Yardım Hattına Artık Almanya Dışından Ulaşılabilir"</item> - <item>"TAN Yardım Hattına Artık Almanya Dışından Ulaşılabilir"</item> + <item>"Sorun Giderme Günlüklerini Kaydet"</item> + <item>"Hızlı Test Profilleri OluÅŸtur"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"PCR testlerinin sonuçlarına ek olarak artık hızlı antijen testlerinin sonuçlarını da uygulamada görüntüleyebilir ve bu sonuçları kullanarak diÄŸer kullanıcıları uyarabilirsiniz."</item> - <item>"Dilerseniz uygulamayı kiÅŸisel enfeksiyon durumunuzu (örneÄŸin, negatif sonuç veren bir hızlı test) kanıtlamak için kullanabilirsiniz. Ancak lütfen unutmayın; enfeksiyon durumunuzu kanıtlamak için uygulamayı kullanmak zorunda deÄŸilsiniz. Enfeksiyon durumunuzu, ikamet ettiÄŸiniz bölgede geçerli olan yasal düzenlemeler uyarınca baÅŸka bir ÅŸekilde de kanıtlayabilirsiniz."</item> - <item>"Teknik yardım hattına artık +49 30 498 75401 numaralı hattı arayarak Almanya dışından da ulaÅŸabilirsiniz. Telefon saÄŸlayıcınız tarafından uygulanan ücretler geçerlidir."</item> - <item>"Koronavirüs tanısı aldıysanız artık +49 30 498 75402 numaralı hattı arayarak Almanya dışından TAN talebinde bulunabilirsiniz. Telefon saÄŸlayıcınız tarafından uygulanan ücretler geçerlidir."</item> + <item>"Artık uygulamanın teknik destek özelliÄŸinde bir hata günlüğü oluÅŸturabilirsiniz. Bu günlük, potansiyel hataların analizini basitleÅŸtirmek ve bu hataları daha hızlı düzeltmemize yardımcı olmak için uygulamada gerçekleÅŸtirdiÄŸiniz adımları kaydeder."</item> + <item>"Artık kiÅŸisel verilerinizde bir hızlı test profili oluÅŸturabilirsiniz. Ardından QR kod ile hızlı ve kolay bir biçimde test noktalarında profiliniz taranabilir."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> <item/> - <item/> - <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> <item/> - <item/> - <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/strings.xml b/Corona-Warn-App/src/main/res/values-tr/strings.xml index ea1fe7e70a82edfff130055bf9c6215115837f45..5a4f718eaf3476317c4046371084b78422767d0b 100644 --- a/Corona-Warn-App/src/main/res/values-tr/strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/strings.xml @@ -64,6 +64,10 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"%s gün önce yüklendi"</string> + <!-- XTXT: risk card - Days since installation if today --> + <string name="risk_card_body_installation_today">"Bugün yüklendi"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"Dün yüklendi"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"Maruz kalma günlüğü son 14 günde %1$s gün etkindi"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -849,7 +853,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"Hata analizi konusunda uygulamanın teknik destek ekibine yardımcı olmak için CWA’da bir hata raporu kaydı oluÅŸturabilirsiniz. OluÅŸturduÄŸunuzda, her bir teknik adım ve uygulama iÅŸlemlerinin sonuçları kaydedilir. Ardından hata raporunu teknik ekibe gönderebilir ve hatanın belirlenip düzeltilmesine yardımcı olabilirsiniz."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"Daha fazla bilgi için lütfen SSS sayfamıza bakın:\nHata raporları için SSS"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"Daha fazla bilgi için lütfen SSS sayfamıza bakın: Hata raporları için SSS"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"Hata raporları için SSS"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -864,10 +868,10 @@ <string name="debugging_debuglog_action_start_recording">"BaÅŸlat"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Durdur ve Sil"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"Hata Raporu Gönder"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Yerel Olarak Kaydet ve Devam Et"</string> + <string name="debugging_debuglog_action_local_log_export">"PaylaÅŸ ve Devam Et"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"Kayıt Etkin"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -894,6 +898,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Analizi Devam Ettir"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"Hata analizini baÅŸlatmak için en az 200 MB bellek alanınızın olması gerekir. Lütfen belleÄŸi boÅŸaltın."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"Hata Raporlarını Gönderme Konusunda Ayrıntılı Bilgi"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1041,6 +1046,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"Bir kiÅŸi akıllı telefon tutuyor. Testteki QR kod kodun taranabileceÄŸini sembolize ediyor."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Onayınız gönüllülük esasına dayalıdır."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"PaylaÅŸmamayı seçseniz de test sonucunuzu alabilirsiniz. Ancak test sonucunuzu paylaşırsanız diÄŸer kullanıcıların enfeksiyona karşı korunmasına yardımcı olursunuz."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"KimliÄŸiniz gizli tutulacaktır. DiÄŸer kullanıcılar, kimin test sonucunu paylaÅŸtığını öğrenmeyecektir."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"“Check-In’lerim†bölümünde check-in yapan konuklarına uyarı gönderilecek etkinlikleri ve yerleri görebilirsiniz. Uyarı iÅŸlemlerinin dışında tutmak için listeden ayrı ayrı check-in’leri kaldırabilirsiniz."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"Onay vermek için 16 yaşın üzerinde olmanız gerekir."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Test Sonucu"</string> @@ -1169,10 +1185,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Koronavirüs testinizin sonucu pozitif olduÄŸu için ÅŸimdi diÄŸer kullanıcıları uygulama üzerinden uyarabilirsiniz."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"Uyarı özelliÄŸi birden fazla ülkede etkindir. Åžu anda aÅŸağıdaki ülkeler katılmaktadır:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Koronavirüs tanısı aldığınız için uygulama üzerinden diÄŸer kullanıcıları uyarabilirsiniz. Hızlı test yaptıysanız uyarı özelliÄŸi yalnızca Almanya’da etkindir. PCR testi yaptırdıysanız uyarı özelliÄŸi ÅŸu ülkelerde etkindir:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1988,9 +2001,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"Geçersiz QR Kod"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Taranan QR kod geçerli deÄŸil.\nLütfen geçerli bir QR kod tarayın.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"Taranan QR kod, etkinlik veya yer check-in’leri için geçerli deÄŸil. \nLütfen uygun bir QR kod tarayın.\n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"Tamam"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Ä°ptal Et"</string> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Uyumsuzluk Uyarısı"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Akıllı telefonunuz Bluetooth üzerinden COVID-19 bildirimlerini yalnızca alabiliyor ancak gönderemiyor. Buna göre, bu arayüz ile maruz kalmalar konusunda uyarı alabilirsiniz ancak siz diÄŸer kullanıcıları uyaramazsınız. Check-in’lerden kaynaklanan uyarıları alabilir ve gönderebilirsiniz."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Akıllı telefonunuz Bluetooth üzerinden COVID-19 bildirimleri gönderemiyor veya alamıyor. Check-in’lerden kaynaklanan uyarıları alabilir ve gönderebilirsiniz."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/antigen_strings.xml b/Corona-Warn-App/src/main/res/values/antigen_strings.xml index 20fbc15d99ddbcc687fb31214d0df16e81c7b0cc..40f157a6f3fc99e6abe1fbba1ef9a347a69fe6b1 100644 --- a/Corona-Warn-App/src/main/res/values/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values/antigen_strings.xml @@ -5,88 +5,88 @@ Homescreen cards - register test card ###################################### --> <!-- XHED: Register test homescreen card: title --> - <string name="ag_homescreen_card_test_register_title">"Test registrieren"</string> + <string name="ag_homescreen_card_test_register_title">"Register Your Test"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Nutzen Sie die App, um Ihre Testergebnisse abrufen und schneller warnen zu können."</string> + <string name="ag_homescreen_card_test_register_body">"Use the app to retrieve your test results, so you can warn others more quickly."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> <!-- XBUT: Homescreen card: show test button --> - <string name="ag_homescreen_card_show_button">"Test anzeigen"</string> + <string name="ag_homescreen_card_show_button">"Display Test"</string> <!-- XBUT: Homescreen card: warn others button --> - <string name="ag_homescreen_card_warn_others_button">"Andere warnen"</string> + <string name="ag_homescreen_card_warn_others_button">"Warn Others"</string> <!-- #################################### Homescreen cards - common status ###################################### --> <!-- XTXT: Homescreen card: status subtitle - no result --> - <string name="ag_homescreen_card_status_no_result">"Ergebnis liegt noch nicht vor"</string> + <string name="ag_homescreen_card_status_no_result">"Your result is not available yet"</string> <!-- XTXT: Homescreen card: status subtitle - result available --> - <string name="ag_homescreen_card_status_result_available">"Testergebnis abrufen"</string> + <string name="ag_homescreen_card_status_result_available">"Retrieve Test Result"</string> <!-- XTXT: Homescreen card: status subtitle - error --> - <string name="ag_homescreen_card_status_error">"Fehlerhafter Test"</string> + <string name="ag_homescreen_card_status_error">"Invalid Test"</string> <!-- XTXT: Homescreen card: status subtitle - invalid --> - <string name="ag_homescreen_card_status_invalid">"Nicht mehr gültig"</string> + <string name="ag_homescreen_card_status_invalid">"No Longer Valid"</string> <!-- XTXT: Homescreen card: status subtitle - findings --> - <string name="ag_homescreen_card_status_findings">"Befund"</string> + <string name="ag_homescreen_card_status_findings">"Findings"</string> <!-- XTXT: Homescreen card: subtitle - corona official name --> <string name="ag_homescreen_card_status_name_of_the_cause_of_this_app">"SARS-CoV-2"</string> <!-- XTXT: Homescreen card: status - negative --> - <string name="ag_homescreen_card_status_negative">"Negativ"</string> + <string name="ag_homescreen_card_status_negative">"Negative"</string> <!-- XTXT: Homescreen card: status - positiv --> - <string name="ag_homescreen_card_status_positiv">"Positiv"</string> + <string name="ag_homescreen_card_status_positiv">"Positive"</string> <!-- #################################### Homescreen cards - common body ###################################### --> <!-- XTXT: homescreen card: body - result available --> - <string name="ag_homescreen_card_body_result_available">"Warnen Sie Ihre Mitmenschen, wenn Sie positiv auf Corona getestet wurden."</string> + <string name="ag_homescreen_card_body_result_available">"If you have tested positive for coronavirus, you can warn others."</string> <!-- XTXT: homescreen card: body - error --> - <string name="ag_homescreen_card_body_error">"Ihr Test konnte nicht ausgewertet werden."</string> + <string name="ag_homescreen_card_body_error">"Your test could not be evaluated."</string> <!-- XTXT: homescreen card: body - not valid test --> - <string name="ag_homescreen_card_body_not_valid_test">"Ihr Test liegt länger als 21 Tage zurück und ist daher nicht länger relevant. Bitte löschen Sie den Test. Danach können Sie einen neuen Test hinzufügen. "</string> + <string name="ag_homescreen_card_body_not_valid_test">"Your test is more than 21 days old and is therefore no longer relevant. Please delete the test. You can then add another. "</string> <!-- XTXT: homescreen card: body - negative --> - <string name="ag_homescreen_card_body_result_negative">"Das Virus SARS-CoV-2 wurde bei Ihnen nicht nachgewiesen."</string> + <string name="ag_homescreen_card_body_result_negative">"You have not been diagnosed with the SARS-CoV-2 virus."</string> <!-- XTXT: homescreen card: body - positive --> - <string name="ag_homescreen_card_body_result_positive">"Das Virus SARS-CoV-2 wurde bei Ihnen nachgewiesen."</string> + <string name="ag_homescreen_card_body_result_positive">"You have been diagnosed with the SARS-CoV-2 virus."</string> <!-- #################################### Homescreen cards - rapid test ###################################### --> <!-- XHED: rapid test homescreen card: title --> - <string name="ag_homescreen_card_rapidtest_title">"Schnelltest"</string> + <string name="ag_homescreen_card_rapidtest_title">"Rapid Test"</string> <!-- XBUT: rapid test homescreen card: dont show anymore button --> - <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Nicht mehr anzeigen"</string> + <string name="ag_homescreen_card_rapidtest_dont_show_anymore_button">"Don’t Display Again"</string> <!-- XTXT: rapid test homescreen card: status subtitle - outdated test --> - <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Test nicht mehr aktuell"</string> + <string name="ag_homescreen_card_rapidtest_status_outdated_test">"Test No Longer Current"</string> <!-- XTXT: rapid test homescreen card: result positive - check results with PCR test --> - <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Machen Sie einen PCR-Test um dieses Test-Ergebnis zu verifizieren."</string> + <string name="ag_homescreen_card_rapidtest_body_result_positive_pcr_check">"Take a PCR test to verify this test result."</string> <!-- Body --> <!-- XTXT: rapid test homescreen card: body - no result --> - <string name="ag_homescreen_card_rapidtest_body_no_result">"Die Auswertung Ihres Schnelltests ist noch nicht abgeschlossen."</string> + <string name="ag_homescreen_card_rapidtest_body_no_result">"The evaluation of your rapid test is not done yet."</string> <!-- XTXT: rapid test homescreen card: body - outdated test --> - <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Ihr Schnelltest ist älter als 48 Stunden und wird hier nicht mehr angezeigt."</string> + <string name="ag_homescreen_card_rapidtest_body_outdated_test">"Your rapid test is more than 48 hours old and will no longer be displayed here."</string> <!-- XTXT: homescreen card: body - negative --> - <string name="ag_homescreen_card_rapid_body_result_date">"Durchgeführt am %1$s"</string> + <string name="ag_homescreen_card_rapid_body_result_date">"Carried out on %1$s"</string> <!-- #################################### Homescreen cards - PCR ###################################### --> <!-- XHED: PCR homescreen card: title --> - <string name="ag_homescreen_card_pcr_title">"PCR-Test"</string> + <string name="ag_homescreen_card_pcr_title">"PCR Test"</string> <!-- XBUT: PCR homescreen card: clear test button --> - <string name="ag_homescreen_card_pcr_clear_test_button">"TEST LÖSCHEN"</string> + <string name="ag_homescreen_card_pcr_clear_test_button">"DELETE TEST"</string> <!-- Body --> <!-- XTXT: PCR homescreen card: body - no result --> - <string name="ag_homescreen_card_pcr_body_no_result">"Die Auswertung Ihres PCR-Tests ist noch nicht abgeschlossen."</string> + <string name="ag_homescreen_card_pcr_body_no_result">"The evaluation of your PCR test is not done yet."</string> <!-- XTXT: homescreen card: body - negative --> - <string name="ag_homescreen_card_pcr_body_result_date">"Test registriert am %1$s"</string> + <string name="ag_homescreen_card_pcr_body_result_date">"Test registered on %1$s"</string> <!-- XBUT: submission deletion warning button continue --> - <string name="submission_deletion_warning_continue_button">"Next"</string> + <string name="submission_deletion_warning_continue_button">"Continue"</string> <!-- XBUT: submission deletion warning button cancel --> <string name="submission_deletion_warning_cancel_button">"Cancel"</string> <!-- XHED: submission deletion warning title --> @@ -101,73 +101,100 @@ <string name="submission_deletion_warning_body_pcr_test">"You have already registered a PCR test. The app can manage a maximum of one rapid test and one PCR test at the same time. If you register another PCR test, the first PCR test will be deleted from the app."</string> <!-- XHED: submission result antigen fragment toolbar text --> - <string name="submission_test_result_toolbar_text">"Ihr Testergebnis"</string> + <string name="submission_test_result_toolbar_text">"Your Test Result"</string> <!-- XHED: submission result antigen fragment card title --> - <string name="submission_test_result_antigen_title">"Schnelltest"</string> + <string name="submission_test_result_antigen_title">"Rapid Test"</string> <!-- XTXT: submission result antigen fragment card "found" text --> - <string name="submission_result_subtitle">"Befund"</string> + <string name="submission_result_subtitle">"Findings"</string> <!-- XTXT: submission test result fragment negative diagnosis --> - <string name="submission_test_result_negative">"Negativ"</string> + <string name="submission_test_result_negative">"Negative"</string> <!-- XTXT: submission test result fragment positive diagnosis --> - <string name="submission_test_result_positive">"Positiv"</string> + <string name="submission_test_result_positive">"Positive"</string> <!-- XTXT: submission result antigen fragment card patient name placeholder --> <string name="submission_test_result_antigen_patient_name_placeholder">"%1$s %2$s"</string> <!-- XTXT: submission result antigen fragment card patient birth date placeholder --> - <string name="submission_test_result_antigen_patient_birth_date_placeholder">"geboren %1$s"</string> + <string name="submission_test_result_antigen_patient_birth_date_placeholder">"Born %1$s"</string> <!-- XTXT: coronatest negative antigen result time and date placeholder --> - <string name="coronatest_negative_antigen_result_time_date_placeholder">"Ausgestellt: %1$s, %2$s Uhr"</string> + <string name="coronatest_negative_antigen_result_time_date_placeholder">"Issued: %1$s, %2$s"</string> <!-- XTXT: submission result antigen fragment card negative result message --> - <string name="submission_test_result_negative_message">"Das Virus SARS-CoV-2 wurde bei Ihnen nicht nachgewiesen."</string> + <string name="submission_test_result_negative_message">"You have not been diagnosed with the SARS-CoV-2 virus."</string> <!-- XHED: submission result antigen fragment negative result proof title --> - <string name="submission_test_result_antigen_negative_proof_title">"Nachweis-Funktion"</string> + <string name="submission_test_result_antigen_negative_proof_title">"Verification Feature"</string> <!-- XHED: submission result antigen fragment negative result proof body --> - <string name="submission_test_result_antigen_negative_proof_body">"Sie können den hier angezeigten Befund auch als Nachweis für das Vorliegen eines negativen Schnelltest-Ergebnisses verwenden.\n\nBitte beachten Sie, dass Sie nur dann einen Nachweis über Ihr Schnelltest-Ergebnis erbringen müssen, wenn dies gesetzlich festgelegt ist. Sie können den Nachweis über die App oder auch auf andere Weise erbringen. Informieren Sie sich hierzu bitte auch über die Kriterien für die Anerkennung von Test-Nachweisen in Ihrem Bundesland."</string> + <string name="submission_test_result_antigen_negative_proof_body">"Your test result is private, although some laws may require you to share it. When required, you may show it via any of the ways provided."</string> <!-- XHED: submission result antigen negative result counter title --> - <string name="submission_test_result_antigen_negative_counter_title">"Ergebnis liegt vor seit"</string> + <string name="submission_test_result_antigen_negative_counter_title">"Result available since"</string> <!-- XHED: coronatest negative antigen result first info title --> - <string name="coronatest_negative_antigen_result_first_info_title">"Ihr Schnelltest wurde hinzugefügt."</string> + <string name="coronatest_negative_antigen_result_first_info_title">"Your rapid test was added."</string> <!-- XTXT: coronatest negative antigen result first info body --> - <string name="coronatest_negative_antigen_result_first_info_body">"Das Test-Ergebnis wird 48 Stunden hier angezeigt. Zusätzlich legt die App einen Eintrag in Ihrem Kontakt-Tagebuch an."</string> + <string name="coronatest_negative_antigen_result_first_info_body">"The test result is displayed here for 48 hours."</string> <!-- XHED: coronatest negative antigen result second info title --> - <string name="coronatest_negative_antigen_result_second_info_title">"Befund negativ"</string> + <string name="coronatest_negative_antigen_result_second_info_title">"Negative Diagnosis"</string> <!-- XTXT: coronatest negative antigen result second info body --> - <string name="coronatest_negative_antigen_result_second_info_body">"Der Schnelltest hat keinen Nachweis für das Coronavirus SARS-CoV-2 bei Ihnen ergeben."</string> + <string name="coronatest_negative_antigen_result_second_info_body">"The rapid test provided no evidence that you have coronavirus SARS-CoV-2."</string> <!-- XTXT: coronatest negative antigen result third info title --> - <string name="coronatest_negative_antigen_result_third_info_title">"Test entfernen"</string> + <string name="coronatest_negative_antigen_result_third_info_title">"Remove Test"</string> <!-- XTXT: coronatest negative antigen result third info body --> - <string name="coronatest_negative_antigen_restul_third_info_body">"Bitte entfernen Sie den Test wieder aus der Corona-Warn-App, damit Sie bei Bedarf einen neuen Test hinterlegen können."</string> + <string name="coronatest_negative_antigen_restul_third_info_body">"Please delete the test from the Corona-Warn-App, so that you can save a new test code here if necessary."</string> <!-- #################################### Rapid Antigen Test Profile ###################################### --> <!-- XHED: Create RAT profile card title --> - <string name="rat_profile_create_card_title">Schnelltest-Profil anlegen</string> + <string name="rat_profile_create_card_title">"Create Rapid Test Profile"</string> <!-- XTXT: Create RAT profile card subtitle --> - <string name="rat_profile_create_card_subtitle">Legen Sie ein Profil mit Ihren persönlichen Daten an, die Sie dann per QR-Code bei jedem Schnelltest vorlegen können.</string> + <string name="rat_profile_create_card_subtitle">"You can create a profile with your personal data, which you can submit via QR code during every rapid test."</string> <!-- XHED: Open RAT profile card title --> - <string name="rat_profile_open_card_title">Schnelltest-Profil</string> + <string name="rat_profile_open_card_title">"Rapid Test Profile"</string> <!-- XTXT: Open RAT profile card subtitle --> - <string name="rat_profile_open_card_subtitle">Sparen Sie Zeit und zeigen Sie Ihr Schnelltest-Profil bei der Teststelle vor.</string> + <string name="rat_profile_open_card_subtitle">"Save time and present your rapid test profile at the testing point."</string> <!-- XHED: Create RAT profile title --> - <string name="rat_profile_create_title">Schnelltest-Profil</string> + <string name="rat_profile_create_title">"Rapid Test Profile"</string> <!-- XTXT: Create RAT profile first name hint --> - <string name="rat_profile_create_first_name_hint">Vorname</string> + <string name="rat_profile_create_first_name_hint">"First Name"</string> <!-- XTXT: Create RAT profile last name hint --> - <string name="rat_profile_create_last_name_hint">Nachname</string> + <string name="rat_profile_create_last_name_hint">"Last Name"</string> <!-- XTXT: Create RAT profile birth date hint --> - <string name="rat_profile_create_birth_date_hint">Geburtsdatum</string> + <string name="rat_profile_create_birth_date_hint">"Date of Birth"</string> <!-- XBUT: Create RAT profile save button --> - <string name="rat_profile_create_button">Speichern</string> + <string name="rat_profile_create_button">"Save"</string> <!-- XTXT: Create RAT profile description --> - <string name="rat_profile_create_description">Legen Sie Ihre persönlichen Daten als QR-Code ab, um die Registrierung an der Teststelle zu beschleunigen.</string> + <string name="rat_profile_create_description">"Save your personal data as a QR code to speed up registration at testing points."</string> <!-- XTXT: Create RAT profile street hint --> - <string name="rat_profile_create_street_hint">Straße und Hausnummer</string> + <string name="rat_profile_create_street_hint">"Street and House Number"</string> <!-- XTXT: Create RAT profile post code hint --> - <string name="rat_profile_create_zip_code_hint">PLZ</string> + <string name="rat_profile_create_zip_code_hint">"Postal Code"</string> <!-- XTXT: Create RAT profile city hint --> - <string name="rat_profile_create_city_hint">Ort</string> + <string name="rat_profile_create_city_hint">"City"</string> <!-- XTXT: Create RAT profile phone hint --> - <string name="rat_profile_create_phone_hint">Telefonnummer</string> + <string name="rat_profile_create_phone_hint">"Phone Number"</string> <!-- XTXT: Create RAT profile email hint --> - <string name="rat_profile_create_email_hint">E-Mail-Adresse</string> -</resources> + <string name="rat_profile_create_email_hint">"E-Mail Address"</string> + + <!--RAT profile onboarding--> + <string name="rat_profile_onboarding_image_content_description">"A woman with a smartphone in her hand is standing in front of a building. A QR code symbolizes the rapid test profile to be scanned."</string> + <!-- XTXT: Create RAT profile onboarding title --> + <string name="rat_profile_onboarding_title">"Save your personal data as a QR code to speed up registration at testing points."</string> + <!-- XTXT: Create RAT profile onboarding subtitle --> + <string name="rat_profile_onboarding_subtitle">"When you create a rapid test profile, you will not have to fill out your personal data again before every rapid test."</string> + <!-- XTXT: Create RAT profile onboarding next button --> + <string name="rat_profile_onboarding_next">"Continue"</string> + <!-- XTXT: Create RAT profile onboarding privacy --> + <string name="rat_profile_onboarding_privacy">"Detailed Information on Data Processing and Your Consent"</string> + <!-- XTXT: Create RAT Qr code profile description--> + <string name="rat_qr_code_profile_description">"Please present this QR code at the testing point so your personal data can be recorded more quickly. Also keep your ID card at hand."</string> + <!-- XTXT: Create RAT Qr code profile birth date--> + <string name="rat_qr_code_profile_birth_date">"Born %1$s"</string> + <!-- XBUT: Create RAT Qr code profile next button--> + <string name="rat_qr_code_profile_next_button">"Continue"</string> + <!-- XTXT: Create RAT Qr code profile delete item--> + <string name="rat_qr_code_profile_delete_item">"Remove"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog title--> + <string name="rat_qr_code_profile_dialog_title">"Do you want to remove the rapid test profile?"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog message--> + <string name="rat_qr_code_profile_dialog_message">"Please consider that if you do, the QR code can no longer be used for registration."</string> + <!-- XTXT: Create RAT Qr code profile delete dialog positive button--> + <string name="rat_qr_code_profile_dialog_positive_button">"Delete"</string> + <!-- XTXT: Create RAT Qr code profile delete dialog negative button--> + <string name="rat_qr_code_profile_dialog_negative_button">"Cancel"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/attrs.xml b/Corona-Warn-App/src/main/res/values/attrs.xml index 21217d0f0c4e970cf73a74ef1e36d9c63e09cd1c..d49dd03c1be761c2fef3f03a4c54f94f8e00aa2c 100644 --- a/Corona-Warn-App/src/main/res/values/attrs.xml +++ b/Corona-Warn-App/src/main/res/values/attrs.xml @@ -43,11 +43,15 @@ <declare-styleable name="MoreInformationView"> <attr name="isTopDividerVisible" format="boolean" /> <attr name="isBottomDividerVisible" format="boolean" /> + <attr name="isToggleVisible" format="boolean" /> <attr name="titleText" format="string" localization="suggested" /> <attr name="subtitleText" format="string" /> + <attr name="toggleOnText" format="string" /> + <attr name="toggleOffText" format="string" /> + <attr name="android:checked" /> </declare-styleable> <declare-styleable name="TraceLocationHighlightView"> <attr name="android:text" /> </declare-styleable> -</resources> \ No newline at end of file +</resources> diff --git a/Corona-Warn-App/src/main/res/values/legal_strings.xml b/Corona-Warn-App/src/main/res/values/legal_strings.xml index 51f899cc94449e5d062bd9b37cb6f11d02c59208..17cccce9fc420f7f1b34faadfd63d0b14007b5c7 100644 --- a/Corona-Warn-App/src/main/res/values/legal_strings.xml +++ b/Corona-Warn-App/src/main/res/values/legal_strings.xml @@ -30,13 +30,15 @@ <!-- YTXT: Body for your consent screen agreement share symptoms --> <string name="submission_your_consent_agreement_share_symptoms" translatable="false">"<b>In the case of rapid tests, only users of the Corona-Warn-App will be warned. If you provide additional information about the onset of symptoms, this will be also shared.</b>\n\nYou can withdraw your consent by disabling “Warn Others†above."</string> <!-- YTXT: Body for your consent screen agreement share symptoms with additional hint for test result--> - <string name="submission_your_consent_agreement_share_symptoms_2" translatable="false"><b>If you provide additional information about the onset of your symptoms, this will also be shared.</b>\n\nYou can withdraw your consent by disabling “Warn others†above. Your test result will then be shown to you.</string> + <string name="submission_your_consent_agreement_share_symptoms_2" translatable="false"><b>In the case of rapid tests, only users of the Corona-Warn-App will be warned. If you provide additional information about the onset of symptoms, this will be also shared.</b>\n\nYou can withdraw your consent by disabling “Warn others†above. Your test result will then be shown to you.</string> <!-- YTXT: Body for keys submission no consent text first part--> <string name="submission_no_consent_your_consent_subsection_body_first_part" translatable="false">"By tapping on “Acceptâ€, you consent to the following:"</string> <!-- YTXT: Body for keys submission no consent text second part--> - <string name="submission_no_consent_your_consent_subsection_body_second_part" translatable="false">"<b>The app will share your test result in order to warn other users of official coronavirus apps whom you have encountered. The warning will work in the countries mentioned above. If you provide additional information about the onset of your symptoms, this will also be shared.</b>In the next step, please share your test result (or more precisely: your random IDs from the last 14 days) by tapping on “Shareâ€."</string> + <string name="submission_no_consent_your_consent_subsection_body_second_part" translatable="false">"<b>The app will share your test result in order to warn users whom you have encountered. This applies to users of coronavirus apps from the above countries and users who were simultaneously checked in at the same event or place as you. In the case of rapid tests, only users of the Corona-Warn-App will be warned. If you provide additional information about the onset of symptoms, this will be also shared.</b>"</string> <!-- YTXT: Body for keys submission no consent text third part--> - <string name="submission_no_consent_your_consent_subsection_body_third_part" translatable="false">"If you have already warned other users, you can withdraw your consent by deleting the app."</string> + <string name="submission_no_consent_your_consent_subsection_body_third_part" translatable="false">"In the next step, grant your consent to access random IDs."</string> + <!-- YTXT: Body for keys submission no consent text fourth part--> + <string name="submission_no_consent_your_consent_subsection_body_fourth_part" translatable="false">"If you have already warned other users, you can withdraw your consent by deleting the app."</string> <!-- XTXT: Title for legal information of the contact diary onboarding screen --> <string name="contact_diary_onboarding_privacy_information_title" translatable="false">"Information on Data Processing"</string> @@ -146,4 +148,22 @@ <string name="trace_location_onboarding_body_consent5" translatable="false">If you later use the warning feature yourself, all of the other users who were checked in at the same time as you at an event or place listed under “My check-ins†will receive a warning based on the event ID.</string> <!-- YMSG: Onboarding trace location bullet points --> <string name="trace_location_onboarding_body_consent6" translatable="false">You can erase a check-in at any time under “My check-insâ€. In this case, you will only be warned if your smartphone identifies a possible exposure after receiving the random IDs of the user who tested positive.</string> + + <!-- RAT Profile Onboarding --> + <!-- XHED: Title for RAT Profile onboarding consent card --> + <string name="rat_profile_onboarding_card_title" translatable="false">Data protection and data security</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent1" translatable="false">Creating a rapid test profile in the app, and using it, are voluntary.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent2" translatable="false">The app converts the data you enter for your rapid test profile into your personal QR code. The QR code then contains all the data you entered.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent3" translatable="false">At a testing point, you can then have your rapid test profile’s QR code scanned as a means of quickly and securely providing your data to the testing point. The testing point will need your data to carry out the test. However, you do not need the app to provide your data. You decide yourself which data is included in the QR code, and which information you communicate to the testing point by other means.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent4" translatable="false">To begin with, only you will have access to your personal rapid test profile. Other people can only access the personal data contained in your rapid test profile if you show them your rapid test profile’s QR code. This will not provide access to other data from the app (e.g. your check-ins or information in your contact journal).</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent5" translatable="false">You have the possibility to delete your rapid test profile at any time. Until then, it will remain stored in the app on your smartphone.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent6" translatable="false">The QR code contains your personal information. Do not provide your personal QR code to anyone if you do not want the data to be read. We do not recommend publishing your personal QR code or sending it by email.</string> + <!-- YMSG: RAT Profile Onboarding bullet points --> + <string name="rat_profile_onboarding_card_consent7" translatable="false">Private individuals as well as companies are not allowed to make you share your rapid test profile with them.</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values/release_info_strings.xml b/Corona-Warn-App/src/main/res/values/release_info_strings.xml index f0effc754445ae3de034dfaa55176b64f545b266..700fee76ef5384fd23a1b7cb2183624ceed8035c 100644 --- a/Corona-Warn-App/src/main/res/values/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values/release_info_strings.xml @@ -17,34 +17,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Registration of Rapid Antigen Tests"</item> - <item>"Display of Proof"</item> - <item>"Technical Hotline Now Available from Outside Germany"</item> - <item>"TAN Hotline Now Available from Outside Germany"</item> + <item>"Record Troubleshooting Logs"</item> + <item>"Create Rapid Test Profiles"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"In addition to the results of PCR tests, you can now also display the results of rapid antigen tests in the app and use them to warn others."</item> - <item>"If you want, you can use the app to prove your personal infection status (a negative rapid test, for example). Please note, however, that you are under no obligation to use the app to prove your infection status. You can also prove your infection status in another manner compliant with the legal regulations at your residence."</item> - <item>"You can now reach the technical hotline from outside Germany as well, under the number +49 30 498 75401. The fees from your telephone provider will apply."</item> - <item>"If you are diagnosed with coronavirus, you can now request a TAN from outside Germany, under the number +49 30 498 75402. The fees from your telephone provider will apply."</item> + <item>"You can now generate an error log in the technical support feature for the app. It records the steps that you perform in the app, to simplify the analysis of potential errors and help us correct them more quickly."</item> + <item>"You can now create a rapid test profile in your personal data. Your profile can then be scanned at testing points quickly and easily via QR code."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item /> <item /> - <item /> - <item /> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item /> <item /> - <item /> - <item /> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 136d577696f2648f5058bdd0d9f9bc11bc4885e6..ba2f0d31fa0e1ad78aa0b3cf6517bc4c62c9e6ef 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -1,13 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> - - <!-- #################################### - Non translatable - ###################################### --> - - <!-- XTXT: Path to the full blown legal html, currently not translated --> - <string name="information_technical_html_path" translatable="false">"technical.html"</string> - <!-- #################################### Generics ###################################### --> @@ -74,9 +66,9 @@ <!-- XTXT: risk card - Days since installation if < 14 days --> <string name="risk_card_body_days_since_installation">"Installed %s days ago"</string> <!-- XTXT: risk card - Days since installation if today --> - <string name="risk_card_body_installation_today"></string> - !-- XTXT: risk card - Days since installation if yesterday --> - <string name="risk_card_body_installation_yesterday"></string> + <string name="risk_card_body_installation_today">"Installed today"</string> + <!-- XTXT: risk card - Days since installation if yesterday --> + <string name="risk_card_body_installation_yesterday">"Installed yesterday"</string> <!-- XTXT: risk card - tracing active for x out of 14 days --> <string name="risk_card_body_saved_days">"Exposure logging was active for %1$s of the past 14 days"</string> <!-- XTXT: risk card- tracing active for 14 out of 14 days --> @@ -862,7 +854,7 @@ <!-- YTXT: Description one for the debug option to record log files --> <string name="debugging_debuglog_intro_explanation_section_one">"To help the app technical support team with error analysis, you can record an error report from the CWA. When you do so, the individual technical steps and results of app processes are recorded. You can then send the error report to technical support and help to identify and correct the error."</string> <!-- YTXT: Description two for the debug option to record log files --> - <string name="debugging_debuglog_intro_explanation_section_two">"For further information, please see our FAQ page:\nFAQ for error reports"</string> + <string name="debugging_debuglog_intro_explanation_section_two">"For further information, please see our FAQ page: FAQ for error reports"</string> <!-- XTXT: Debug Log screen increased risk level link label - HAS TO MATCH the link text above --> <string name="debugging_debuglog_intro_explanation_section_two_link_label">"FAQ for error reports"</string> <!-- XTXT: Explains user about about debug log: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> @@ -877,10 +869,10 @@ <string name="debugging_debuglog_action_start_recording">"Start"</string> <!-- XBUT: Button text to stop the log recording --> <string name="debugging_debuglog_action_stop_recording">"Stop and Delete"</string> - <!-- XBUT: Button text to send the log recording to the server --> + <!-- XBUT: Button text to send the log recording to the server--> <string name="debugging_debuglog_action_share_log">"Send Error Report"</string> <!-- XBUT: Button text to export the log --> - <string name="debugging_debuglog_action_local_log_export">"Save Locally and Continue"</string> + <string name="debugging_debuglog_action_local_log_export">"Share and Continue"</string> <!-- YTXT: Status text if a debug log is being recorded --> <string name="debugging_debuglog_status_recording">"Recording Active"</string> <!-- YTXT: Status text if a debug log is being recorded but there is not enough free storage space --> @@ -907,6 +899,7 @@ <string name="debugging_debuglog_stop_confirmation_discard_button">"Continue Analysis"</string> <!-- YTXT: Dialog message if there is not enough free storage to start a debug log --> <string name="debugging_debuglog_start_low_storage_error">"You need at least 200 MB of memory to start the error analysis. Please free up memory."</string> + <!-- XHED: Title for debug legal screen --> <string name="debugging_debuglog_legal_dialog_title">"Detailed Information on Sending Error Reports"</string> <!-- YTXT: Section Title for debug legal screen --> @@ -1054,6 +1047,17 @@ <!-- YTXT: Content Description for the illustration --> <string name="submission_consent_main_illustration_description">"A person is holding a smartphone. A QR code on a test symbolizes the code to be scanned."</string> + <!-- YTXT: Body for no consent section first point --> + <string name="submission_no_consent_first_point">"Your consent is voluntary."</string> + <!-- YTXT: Body for no consent section second point --> + <string name="submission_no_consent_second_point">"You can retrieve your test result even if you do not choose to share it. But if you share your test result, you will help to protect others against infection."</string> + <!-- YTXT: Body for no consent section third point --> + <string name="submission_no_consent_third_point">"Your identity will remain secret. Other users will not find out who has shared a test result."</string> + <!-- YTXT: Body for no consent section fourth point --> + <string name="submission_no_consent_fourth_point">"Under “My Check-Insâ€, you can see the events and places whose checked-in guests will be warned. You can remove individual check-ins from the list to exclude them from the warning process."</string> + <!-- YTXT: Body for no consent section fifth point --> + <string name="submission_no_consent_fifth_point">"You must be at least 16 years old to grant your consent."</string> + <!-- Submission Test Result --> <!-- XHED: Page headline for test result --> <string name="submission_test_result_headline">"Test Result"</string> @@ -1182,10 +1186,7 @@ <!-- Submission Positive Other Warning No Consent --> <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_first_part">"Because you tested positive for coronavirus, you can now warn others through the app."</string> - <!-- YTXT: Body text for the positive result additional warning page--> - <string name="submission_positive_other_warning_no_consent_body_second_part">"The warning feature works in several countries. The following countries are currently participating:"</string> - + <string name="submission_positive_other_warning_no_consent_body_first_part">"Because you have been diagnosed with coronavirus, you can warn others through the app. If you took a rapid test, the warning feature only works in Germany. If you took a PCR test, the warning feature works in the following countries:"</string> <!-- Test result positive and no consent given --> <!-- XHED: Title for test result positive and no consent given--> @@ -1948,16 +1949,7 @@ <!-- XACT: Button/Dialog label to cancel something--> <string name="generic_action_abort">"Cancel"</string> <!-- XACT: Button/Dialog label to remove something--> - <string name="generic_action_remove">Remove</string> - - <!-- #################################### - Incompatibility warning card - ###################################### --> - <string name="incompatible_headline"></string> - <string name="incompatible_advertising_not_supported"></string> - <string name="incompatible_scanning_not_supported"></string> - - <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> + <string name="generic_action_remove">"Remove"</string> <!-- #################################### Trace Location @@ -2010,9 +2002,22 @@ <!-- XHED: Trace location check-ins invalid qr code dialog title --> <string name="trace_location_attendee_invalid_qr_code_dialog_title">"Invalid QR Code"</string> <!-- YTXT: Trace location check-ins invalid qr code dialog message --> - <string name="trace_location_attendee_invalid_qr_code_dialog_message">"The scanned QR code is not valid.\nPlease scan a valid QR code.\n\n(%1$s)"</string> + <string name="trace_location_attendee_invalid_qr_code_dialog_message">"The scanned QR code is not valid for check-ins for an event or place. \nPlease scan a suitable QR code.\n\n(%1$s)"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog positive button --> <string name="trace_location_attendee_invalid_qr_code_dialog_positive_button">"OK"</string> <!-- XBUT: Trace location check-ins invalid qr code dialog negative button --> <string name="trace_location_attendee_invalid_qr_code_dialog_negative_button">"Cancel"</string> -</resources> + + <!-- #################################### + Incompatibility warning card + ###################################### --> + + <!-- XHED: Incompitability card title --> + <string name="incompatible_headline">"Incompatibility Warning"</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_advertising_not_supported">"Your smartphone can only receive COVID-19 notifications via Bluetooth, but not send them. This means you can be warned of exposures through this interface, but you cannot warn others of exposures yourself. You can send and receive warnings that result from check-ins."</string> + <!-- XTXT: Incompitability card content --> + <string name="incompatible_scanning_not_supported">"Your smartphone cannot send or receive COVID-19 notifications via Bluetooth. You can send and receive warnings that result from check-ins."</string> + <!-- XTXT: Incompitability faq link --> + <string name="incompatible_link">"https://www.coronawarn.app/en/faq/#incompatibility_warning"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index be7743ea4251a325806bf29993af49ddbaaeba5d..dbdaf8ec64842f97a24b27b26a8ef05293f79b64 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -83,6 +83,14 @@ <item name="android:background">@color/colorTransparent</item> </style> + <style name="CWAToolbar.Theme" parent="Widget.AppCompat.ActionBar"> + <item name="actionOverflowButtonStyle">@style/OverflowButtonStyle</item> + </style> + + <style name="OverflowButtonStyle" parent="Widget.AppCompat.Light.ActionButton.Overflow"> + <item name="android:tint">@android:color/white</item> + </style> + <!-- Dialog Theme--> <style name="DialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog.Alert"> <item name="buttonBarPositiveButtonStyle">@style/DialogButtonTheme</item> @@ -243,8 +251,7 @@ <style name="cardTracing"> <item name="android:padding">@dimen/card_padding</item> - <item name="android:background">@drawable/card</item> - <item name="android:backgroundTint">@color/colorSurface2</item> + <item name="android:background">@drawable/card_dark</item> </style> <style name="selectionButton" parent="@style/Widget.AppCompat.Button.Borderless"> @@ -283,6 +290,7 @@ <style name="headline4" parent="@style/TextAppearance.MaterialComponents.Headline4"> <item name="android:textColor">@color/colorTextPrimary1</item> </style> + <style name="headline4Bold" parent="@style/headline4"> <item name="android:textStyle">bold</item> </style> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt index c0680a991b0dae9908b46274bac5b5bbd4988276..988042877fb6032ffa037c2ee5957c30ec905798 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/BugCensorTest.kt @@ -54,6 +54,51 @@ class BugCensorTest : BaseTest() { BugCensor.withValidComment("abc") {} shouldBe true } + @Test + fun `description censoring validity`() { + BugCensor.withValidDescription(null) {} shouldBe false + BugCensor.withValidDescription(" ") {} shouldBe false + BugCensor.withValidDescription(" ") {} shouldBe false + BugCensor.withValidDescription("a") {} shouldBe false + BugCensor.withValidDescription("ab") {} shouldBe false + BugCensor.withValidDescription("abc") {} shouldBe false + BugCensor.withValidDescription("abcd") {} shouldBe false + BugCensor.withValidDescription("abcde") {} shouldBe true + } + + @Test + fun `address censoring validity`() { + BugCensor.withValidAddress(null) {} shouldBe false + BugCensor.withValidAddress(" ") {} shouldBe false + BugCensor.withValidAddress(" ") {} shouldBe false + BugCensor.withValidAddress("a") {} shouldBe false + BugCensor.withValidAddress("ab") {} shouldBe false + BugCensor.withValidAddress("abc") {} shouldBe false + BugCensor.withValidAddress("abcd") {} shouldBe true + } + + @Test + fun `city censoring validity`() { + BugCensor.withValidCity(null) {} shouldBe false + BugCensor.withValidCity(" ") {} shouldBe false + BugCensor.withValidCity(" ") {} shouldBe false + BugCensor.withValidCity("a") {} shouldBe false + BugCensor.withValidCity("ab") {} shouldBe false + BugCensor.withValidCity("abc") {} shouldBe true + } + + @Test + fun `zip-code censoring validity`() { + BugCensor.withValidZipCode(null) {} shouldBe false + BugCensor.withValidZipCode(" ") {} shouldBe false + BugCensor.withValidZipCode(" ") {} shouldBe false + BugCensor.withValidZipCode("1") {} shouldBe false + BugCensor.withValidZipCode("12") {} shouldBe false + BugCensor.withValidZipCode("123") {} shouldBe false + BugCensor.withValidZipCode("1234") {} shouldBe false + BugCensor.withValidZipCode("12345") {} shouldBe true + } + @Test fun `loglines are only copied if the message is different`() { val logLine = LogLine( diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt index e6c3aa4a1df8bf4db40f25f4eb488cf3ce9150df..cf4989e79c22668f3f03905bd2d0845dd589b5f8 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CensorInjectionTest.kt @@ -6,6 +6,9 @@ import dagger.Provides import de.rki.coronawarnapp.bugreporting.BugReportingSharedModule import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository import de.rki.coronawarnapp.submission.SubmissionSettings import io.github.classgraph.ClassGraph import io.kotest.matchers.collections.shouldContainAll @@ -73,4 +76,16 @@ class MockProvider { @Singleton @Provides fun coronaTestRepository(): CoronaTestRepository = mockk() + + @Singleton + @Provides + fun checkInRepository(): CheckInRepository = mockk() + + @Singleton + @Provides + fun traceLocationRepository(): TraceLocationRepository = mockk() + + @Singleton + @Provides + fun ratProfileSettings(): RATProfileSettings = mockk() } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..818cdbb8e7e03ed2b86cb1cfce7ea7c8e59a0245 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CheckInsCensorTest.kt @@ -0,0 +1,139 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.CheckInsCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class CheckInsCensorTest : BaseTest() { + + @MockK lateinit var checkInsRepo: CheckInRepository + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = CheckInsCensor( + debugScope = scope, + checkInRepository = checkInsRepo + ) + + private fun mockCheckIn( + checkInId: Long, + checkInDescription: String, + checkInAddress: String + ) = mockk<CheckIn>().apply { + every { id } returns checkInId + every { description } returns checkInDescription + every { address } returns checkInAddress + } + + @Test + fun `checkLog() should return LogLine with censored check-in information`() = runBlocking { + every { checkInsRepo.allCheckIns } returns flowOf( + listOf( + mockCheckIn( + checkInId = 1, + checkInDescription = "Moe's Tavern", + checkInAddress = "Near 742 Evergreen Terrace, 12345 Springfield" + ), + mockCheckIn( + checkInId = 2, + checkInDescription = "Kwik-E-Mart", + checkInAddress = "Some Street, 12345 Springfield" + ) + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + Let's go to Moe's Tavern in Near 742 Evergreen Terrace, 12345 Springfield. + Who needs the Kwik-E-Mart in Some Street, 12345 Springfield? I doooo! + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Let's go to CheckIn#1/Description in CheckIn#1/Address. + Who needs the CheckIn#2/Description in CheckIn#2/Address? I doooo! + """.trimIndent() + ) + + // censoring should still work after user deletes his check-ins + every { checkInsRepo.allCheckIns } returns flowOf(emptyList()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Let's go to CheckIn#1/Description in CheckIn#1/Address. + Who needs the CheckIn#2/Description in CheckIn#2/Address? I doooo! + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return null if no check-ins are stored`() = runBlocking { + every { checkInsRepo.allCheckIns } returns flowOf(emptyList()) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Some log message that shouldn't be censored.", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + + every { checkInsRepo.allCheckIns } returns flowOf( + listOf( + mockCheckIn( + checkInId = 1, + checkInDescription = "Description 1", + checkInAddress = "Address 1" + ), + mockCheckIn( + checkInId = 2, + checkInDescription = "Description 2", + checkInAddress = "Address 2" + ) + ) + ) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Some log message that shouldn't be censored.", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt similarity index 54% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt index b3fde2e4884541ee4687eaa2afd73854c3e0d2bb..0b88f1988898763b4688396469c4dccad04c8b47 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/CoronaTestCensorTest.kt @@ -1,8 +1,11 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest import de.rki.coronawarnapp.util.CWADebug import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -17,15 +20,22 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -class RegistrationTokenCensorTest : BaseTest() { +class CoronaTestCensorTest : BaseTest() { @MockK lateinit var coronaTestRepository: CoronaTestRepository private val testToken = "63b4d3ff-e0de-4bd4-90c1-17c2bb683a2f" + private val pcrIdentifier = "qrcode-pcr-someIdentifier" + private val ratIdentifier = "qrcode-rat-someIdentifier" private val coronaTests: MutableStateFlow<Set<CoronaTest>> = MutableStateFlow( setOf( - mockk<CoronaTest>().apply { + mockk<PCRCoronaTest>().apply { every { registrationToken } returns testToken + every { identifier } returns pcrIdentifier + }, + mockk<RACoronaTest>().apply { + every { registrationToken } returns testToken + every { identifier } returns ratIdentifier } ) ) @@ -40,7 +50,7 @@ class RegistrationTokenCensorTest : BaseTest() { every { coronaTestRepository.coronaTests } returns coronaTests } - private fun createInstance() = RegistrationTokenCensor( + private fun createInstance() = CoronaTestCensor( coronaTestRepository = coronaTestRepository ) @@ -50,31 +60,31 @@ class RegistrationTokenCensorTest : BaseTest() { val filterMe = LogLine( timestamp = 1, priority = 3, - message = "I'm a shy registration token: $testToken", + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", tag = "I'm a tag", throwable = null ) instance.checkLog(filterMe) shouldBe filterMe.copy( - message = "I'm a shy registration token: ########-####-####-####-########3a2f" + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" ) every { CWADebug.isDeviceForTestersBuild } returns true instance.checkLog(filterMe) shouldBe filterMe.copy( - message = "I'm a shy registration token: ########-e0de-4bd4-90c1-17c2bb683a2f" + message = "I'm a shy registration token: ########-e0de-4bd4-90c1-17c2bb683a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" ) verify { coronaTestRepository.coronaTests } } @Test - fun `censoring returns null if there is no token`() = runBlockingTest { + fun `censoring returns null if there is no corona test stored`() = runBlockingTest { coronaTests.value = emptySet() val instance = createInstance() val filterMeNot = LogLine( timestamp = 1, priority = 3, - message = "I'm a shy registration token: $testToken", + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", tag = "I'm a tag", throwable = null ) @@ -93,4 +103,29 @@ class RegistrationTokenCensorTest : BaseTest() { ) instance.checkLog(filterMeNot) shouldBe null } + + @Test + fun `censoring still works after test was deleted`() = runBlockingTest { + + val censor = createInstance() + + val filterMe = LogLine( + timestamp = 1, + priority = 3, + message = "I'm a shy registration token: $testToken and we are extrovert $pcrIdentifier and $ratIdentifier", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(filterMe) shouldBe filterMe.copy( + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" + ) + + // delete all tests + coronaTests.value = emptySet() + + censor.checkLog(filterMe) shouldBe filterMe.copy( + message = "I'm a shy registration token: ########-####-####-####-########3a2f and we are extrovert qrcode-pcr-CoronaTest/Identifier and qrcode-rat-CoronaTest/Identifier" + ) + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt index a00968c428c93e723309761c38e08af1634ce905..fead49fe948f4b9fc1d76262ba6fe22651ba2830 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryEncounterCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryEncounterCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -71,6 +72,17 @@ class DiaryEncounterCensorTest : BaseTest() { everyone disliked that. """.trimIndent() ) + + // censoring should still work after encounters are deleted + every { diaryRepo.personEncounters } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + On Encounter#2/Circumstances, + two persons Encounter#3/Circumstances, + everyone disliked that. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt index 0c80fee73ed0eb5e43ee044c80a43214c73047d2..191afcd5dcace16a5dce810c8aeeb8b6a9d2cbf4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryLocationCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryLocationCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -73,6 +74,17 @@ class DiaryLocationCensorTest : BaseTest() { and that Location#2/Name doesn't exist as it has neither phonenumber (null) nor email (null). """.trimIndent() ) + + // censoring should still work after locations are deleted + every { diaryRepo.locations } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + Bürgermeister of Location#1/Name (Location#1/PhoneNumber) and Karl of Location#3/Name [Location#3/PhoneNumber] called each other. + Both agreed that their emails (Location#1/EMail|Location#3/EMail) are awesome, + and that Location#2/Name doesn't exist as it has neither phonenumber (null) nor email (null). + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt index e4e5e38c294d6f3b384f6a8e7191f725a29ed441..1025df09c8876fd49a7e11f73c47603a77cb265f 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryPersonCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryPersonCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -74,6 +75,17 @@ class DiaryPersonCensorTest : BaseTest() { A quick mail to Person#1/EMail confirmed this. """.trimIndent() ) + + // censoring should still work after people are deleted + every { diaryRepo.people } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + Person#2/Name requested more coffee from Person#1/PhoneNumber, + but Person#3/Name thought he had enough has had enough for today. + A quick mail to Person#1/EMail confirmed this. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt index cb9405722fde29918cb4d4a03fff8413fd518bea..5c8a0ed5155bd74d96a1f685b60fbc362ab68ccb 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/DiaryVisitCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.contactdiary.DiaryVisitCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository @@ -69,6 +70,17 @@ class DiaryVisitCensorTest : BaseTest() { only to find out the supermarket was Visit#3/Circumstances. """.trimIndent() ) + + // censoring should still work even after visits are deleted + every { diaryRepo.locationVisits } returns flowOf(emptyList()) + instance.checkLog(censorMe) shouldBe censorMe.copy( + message = + """ + After having a Visit#1/Circumstances, + I got my Visit#2/Circumstances, + only to find out the supermarket was Visit#3/Circumstances. + """.trimIndent() + ) } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt similarity index 86% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt index 62bf33f114bc5d0051b5d9fa0fafa92264c01417..d89f76791836507ba93d111cbf1d10fb776b7743 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/QRCodeCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/PcrQrCodeCensorTest.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.bugreporting.censors +import de.rki.coronawarnapp.bugreporting.censors.submission.PcrQrCodeCensor import de.rki.coronawarnapp.bugreporting.debuglog.LogLine import de.rki.coronawarnapp.util.CWADebug import io.kotest.matchers.shouldBe @@ -12,7 +13,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -class QRCodeCensorTest : BaseTest() { +class PcrQrCodeCensorTest : BaseTest() { private val testGUID = "63b4d3ff-e0de-4bd4-90c1-17c2bb683a2f" @@ -26,14 +27,14 @@ class QRCodeCensorTest : BaseTest() { @AfterEach fun teardown() { - QRCodeCensor.lastGUID = null + PcrQrCodeCensor.lastGUID = null } - private fun createInstance() = QRCodeCensor() + private fun createInstance() = PcrQrCodeCensor() @Test fun `censoring replaces the logline message`() = runBlockingTest { - QRCodeCensor.lastGUID = testGUID + PcrQrCodeCensor.lastGUID = testGUID val instance = createInstance() val censored = LogLine( timestamp = 1, @@ -54,7 +55,7 @@ class QRCodeCensorTest : BaseTest() { @Test fun `censoring returns null if there is no match`() = runBlockingTest { - QRCodeCensor.lastGUID = testGUID.replace("f", "a") + PcrQrCodeCensor.lastGUID = testGUID.replace("f", "a") val instance = createInstance() val notCensored = LogLine( timestamp = 1, @@ -68,7 +69,7 @@ class QRCodeCensorTest : BaseTest() { @Test fun `censoring aborts if no qrcode was set`() = runBlockingTest { - QRCodeCensor.lastGUID = null + PcrQrCodeCensor.lastGUID = null val instance = createInstance() val notCensored = LogLine( timestamp = 1, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..74c66acfadeafe2a4252b0a94df2457ebd7806bc --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RACoronaTestCensorTest.kt @@ -0,0 +1,121 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.submission.RACoronaTestCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.joda.time.LocalDate +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class RACoronaTestCensorTest : BaseTest() { + + @MockK lateinit var coronaTestRepository: CoronaTestRepository + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = RACoronaTestCensor( + debugScope = scope, + coronaTestRepository = coronaTestRepository + ) + + @Test + fun `checkLog() should return censored LogLine`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf( + setOf( + mockk<RACoronaTest>().apply { + every { firstName } returns "John" + every { lastName } returns "Doe" + every { dateOfBirth } returns LocalDate.parse("2020-01-01") + } + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + Hello! My name is John. My friends call me Mister Doe and I was born on 2020-01-01. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Hello! My name is RATest/FirstName. My friends call me Mister RATest/LastName and I was born on RATest/DateOfBirth. + """.trimIndent() + ) + + // censoring should still work when test gets deleted + every { coronaTestRepository.coronaTests } returns flowOf(emptySet()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + Hello! My name is RATest/FirstName. My friends call me Mister RATest/LastName and I was born on RATest/DateOfBirth. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return return null if no corona tests are stored`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf(emptySet()) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + every { coronaTestRepository.coronaTests } returns flowOf( + setOf( + mockk<RACoronaTest>().apply { + every { firstName } returns "John" + every { lastName } returns "Doe" + every { dateOfBirth } returns LocalDate.parse("2020-01-01") + } + ) + ) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `censoring should still work when test gets deleted`() { + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..83b890cd184c083563c5b1c517362e2cd3768d68 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/RatQrCodeCensorTest.kt @@ -0,0 +1,103 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.submission.RatQrCodeCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.util.CWADebug +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.mockkObject +import kotlinx.coroutines.test.runBlockingTest +import org.joda.time.LocalDate +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class RatQrCodeCensorTest { + + private val testHash = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + private val testRawString = "testRawString" + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + + mockkObject(CWADebug) + every { CWADebug.isDeviceForTestersBuild } returns false + } + + @AfterEach + fun teardown() { + RatQrCodeCensor.dataToCensor = null + } + + private fun createInstance() = RatQrCodeCensor() + + @Test + fun `checkLog() should return censored LogLine`() = runBlockingTest { + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = testRawString, + hash = testHash, + firstName = "Milhouse", + lastName = "Van Houten", + dateOfBirth = LocalDate.parse("1980-07-01") + ) + + val censor = createInstance() + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash of the rat test of Milhouse Van Houten. He was born on 1980-07-01", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = "Here comes the hash: SHA256HASH-ENDING-WITH-15ad of the rat test of RATest/FirstName RATest/LastName. He was born on RATest/DateOfBirth" + ) + + every { CWADebug.isDeviceForTestersBuild } returns true + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = "Here comes the hash: SHA256HASH-ENDING-WITH-61a396177a9cb410ff61f20015ad of the rat test of RATest/FirstName RATest/LastName. He was born on RATest/DateOfBirth" + ) + } + + @Test + fun `checkLog() should return null if no data to censor was set`() = runBlockingTest { + val censor = createInstance() + + val logLineNotToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineNotToCensor) shouldBe null + } + + @Test + fun `checkLog() should return null if nothing should be censored`() = runBlockingTest { + RatQrCodeCensor.dataToCensor = RatQrCodeCensor.CensorData( + rawString = testRawString, + hash = testHash.replace("8", "9"), + firstName = null, + lastName = null, + dateOfBirth = null + ) + + val censor = createInstance() + + val logLineNotToCensor = LogLine( + timestamp = 1, + priority = 3, + message = "Here comes the hash: $testHash", + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineNotToCensor) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..c7f6d6b7f41e7d877a2a0842a8051153fc765a8c --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/TraceLocationCensorTest.kt @@ -0,0 +1,189 @@ +package de.rki.coronawarnapp.bugreporting.censors + +import de.rki.coronawarnapp.bugreporting.censors.presencetracing.TraceLocationCensor +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation +import de.rki.coronawarnapp.presencetracing.locations.TraceLocationUserInput +import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository +import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class TraceLocationCensorTest : BaseTest() { + + @MockK lateinit var traceLocationRepo: TraceLocationRepository + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + } + + @AfterEach + fun teardown() { + TraceLocationCensor.dataToCensor = null + } + + private fun createInstance(scope: CoroutineScope) = TraceLocationCensor( + debugScope = scope, + traceLocationRepository = traceLocationRepo + ) + + private fun mockTraceLocation( + traceLocationId: Long, + traceLocationType: TraceLocationOuterClass.TraceLocationType, + traceLocationDescription: String, + traceLocationAddress: String, + ) = mockk<TraceLocation>().apply { + every { id } returns traceLocationId + every { type } returns traceLocationType + every { description } returns traceLocationDescription + every { address } returns traceLocationAddress + } + + @Test + fun `checkLog() should return LogLine with censored trace location information from repository`() = runBlocking { + every { traceLocationRepo.allTraceLocations } returns flowOf( + listOf( + mockTraceLocation( + traceLocationId = 1, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_PERMANENT_FOOD_SERVICE, + traceLocationDescription = "Sushi Place", + traceLocationAddress = "Sushi Street 123, 12345 Fish Town" + ), + mockTraceLocation( + traceLocationId = 2, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Rick Astley Concert", + traceLocationAddress = "Never gonna give you up street 1, 12345 RickRoll City" + ) + ) + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + The type is LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT. Yesterday we went to the Rick Astley Concert. The spectacle took place in Never gonna give you up street 1, 12345 RickRoll City. + Afterwards we had some food in Sushi Place in Sushi Street 123, 12345 Fish Town. It a nice LOCATION_TYPE_PERMANENT_FOOD_SERVICE. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The type is TraceLocation#2/Type. Yesterday we went to the TraceLocation#2/Description. The spectacle took place in TraceLocation#2/Address. + Afterwards we had some food in TraceLocation#1/Description in TraceLocation#1/Address. It a nice TraceLocation#1/Type. + """.trimIndent() + ) + + // censoring should still work after the user deletes his trace locations + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The type is TraceLocation#2/Type. Yesterday we went to the TraceLocation#2/Description. The spectacle took place in TraceLocation#2/Address. + Afterwards we had some food in TraceLocation#1/Description in TraceLocation#1/Address. It a nice TraceLocation#1/Type. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return LogLine with censored trace location information from companion object`() = + runBlocking { + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + TraceLocationCensor.dataToCensor = TraceLocationUserInput( + type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT, + description = "Top Secret Private Event", + address = "top secret address", + startDate = null, + endDate = null, + defaultCheckInLengthInMinutes = 180 + ) + + val censor = createInstance(this) + + val logLineToCensor = LogLine( + timestamp = 1, + priority = 3, + message = + """ + The user just created a new traceLocation with Top Secret Private Event as the description and + top secret address as the address. The type is LOCATION_TYPE_TEMPORARY_PRIVATE_EVENT. + """.trimIndent(), + tag = "I am tag", + throwable = null + ) + + censor.checkLog(logLineToCensor) shouldBe logLineToCensor.copy( + message = + """ + The user just created a new traceLocation with TraceLocationUserInput#Description as the description and + TraceLocationUserInput#Address as the address. The type is TraceLocationUserInput#Type. + """.trimIndent() + ) + } + + @Test + fun `checkLog() should return null if no trace locations are stored`() = runBlockingTest { + every { traceLocationRepo.allTraceLocations } returns flowOf(emptyList()) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlockingTest { + + every { traceLocationRepo.allTraceLocations } returns flowOf( + listOf( + mockTraceLocation( + traceLocationId = 1, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Description 1", + traceLocationAddress = "Address 1" + ), + mockTraceLocation( + traceLocationId = 2, + traceLocationType = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT, + traceLocationDescription = "Description 2", + traceLocationAddress = "Address 2" + ) + ) + ) + + val censor = createInstance(this) + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f3c887739c3fc236ec281c5a69b8db69bf5f06a8 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/submission/RatProfileCensorTest.kt @@ -0,0 +1,106 @@ +package de.rki.coronawarnapp.bugreporting.censors.submission + +import de.rki.coronawarnapp.bugreporting.debuglog.LogLine +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfile +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.joda.time.format.DateTimeFormat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +internal class RatProfileCensorTest : BaseTest() { + + @MockK lateinit var ratProfileSettings: RATProfileSettings + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + private fun createInstance(scope: CoroutineScope) = RatProfileCensor( + ratProfileSettings = ratProfileSettings + ) + + @Test + fun `checkLog() should return null if no RAT profile is stored`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(null) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return null if LogLine doesn't need to be censored`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(profile) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Lorem ipsum", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe null + } + + @Test + fun `checkLog() should return censored LogLine`() = runBlocking { + every { ratProfileSettings.profile.flow } returns flowOf(profile) + + val censor = createInstance(this) + + val logLine = LogLine( + timestamp = 1, + priority = 3, + message = "Mister First name who is also known as Last name and is born on 1950-08-01 lives in Main street, " + + "12132 in the beautiful city of London. You can reach him by phone: 111111111 or email: email@example.com", + tag = "I'm a tag", + throwable = null + ) + + censor.checkLog(logLine) shouldBe logLine.copy( + message = "Mister RAT-Profile/FirstName who is also known as RAT-Profile/LastName and is born on RAT-Profile/DateOfBirth lives in RAT-Profile/Street, " + + "RAT-Profile/Zip-Code in the beautiful city of RAT-Profile/City. You can reach him by phone: RAT-Profile/Phone or email: RAT-Profile/eMail" + ) + + // censoring should still work after the user deletes his profile + every { ratProfileSettings.profile.flow } returns flowOf(null) + + censor.checkLog(logLine) shouldBe logLine.copy( + message = "Mister RAT-Profile/FirstName who is also known as RAT-Profile/LastName and is born on RAT-Profile/DateOfBirth lives in RAT-Profile/Street, " + + "RAT-Profile/Zip-Code in the beautiful city of RAT-Profile/City. You can reach him by phone: RAT-Profile/Phone or email: RAT-Profile/eMail" + ) + } + + private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd") + + private val profile = RATProfile( + firstName = "First name", + lastName = "Last name", + birthDate = formatter.parseLocalDate("1950-08-01"), + street = "Main street", + zipCode = "12132", + city = "London", + phone = "111111111", + email = "email@example.com" + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt index ace9d7c0a8bf6eacb9e3f12d265f0f0a157d593a..029f51ee528a097dd1be66fd1158be739dfd4588 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/debuglog/DebugLoggerTest.kt @@ -2,7 +2,7 @@ package de.rki.coronawarnapp.bugreporting.debuglog import android.app.Application import dagger.Lazy -import de.rki.coronawarnapp.bugreporting.censors.RegistrationTokenCensor +import de.rki.coronawarnapp.bugreporting.censors.submission.CoronaTestCensor import de.rki.coronawarnapp.bugreporting.debuglog.internal.DebugLogTree import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.di.ApplicationComponent @@ -29,7 +29,7 @@ class DebugLoggerTest : BaseIOTest() { @MockK lateinit var application: Application @MockK lateinit var component: ApplicationComponent - @MockK lateinit var registrationTokenCensor: RegistrationTokenCensor + @MockK lateinit var coronaTestCensor: CoronaTestCensor private val testDir = File(IO_TEST_BASEDIR, this::class.simpleName!!) private val cacheDir = File(testDir, "cache") @@ -49,10 +49,10 @@ class DebugLoggerTest : BaseIOTest() { every { application.cacheDir } returns cacheDir every { component.inject(any<DebugLogger>()) } answers { val logger = arg<DebugLogger>(0) - logger.bugCensors = Lazy { setOf(registrationTokenCensor) } + logger.bugCensors = Lazy { setOf(coronaTestCensor) } } - coEvery { registrationTokenCensor.checkLog(any()) } returns null + coEvery { coronaTestCensor.checkLog(any()) } returns null } @AfterEach diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt index f17d6c08cf076ebf3b5bd7e82a80c89a1d9ba8b9..86f776afc70b37dec997498d0e3d1c3f84eec90f 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt @@ -17,6 +17,7 @@ import de.rki.coronawarnapp.contactdiary.util.ContactDiaryData import de.rki.coronawarnapp.contactdiary.util.mockStringsForContactDiaryExporterTests import de.rki.coronawarnapp.presencetracing.checkins.CheckIn import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.checkins.common.locationName import de.rki.coronawarnapp.presencetracing.risk.TraceLocationCheckInRisk import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk @@ -81,16 +82,34 @@ open class ContactDiaryOverviewViewModelTest { private val personEncounter = DefaultContactDiaryPersonEncounter(125, date, person) private val locationVisit = DefaultContactDiaryLocationVisit(126, date, location) + private val checkInLow = mockk<CheckIn>().apply { + every { id } returns 147 + every { traceLocationId } returns "12ab-34cd-56ef-78gh-456".decodeBase64()!! + every { description } returns "Jahrestreffen der deutschen SAP Anwendergruppe" + every { address } returns "Hauptstr 3, 69115 Heidelberg" + every { traceLocationStart } returns null + every { traceLocationEnd } returns null + } + + private val checkInHigh = mockk<CheckIn>().apply { + every { id } returns 148 + every { traceLocationId } returns "12ab-34cd-56ef-78gh-457".decodeBase64()!! + every { description } returns "Kiosk" + every { address } returns "Hauptstr 4, 69115 Heidelberg" + every { traceLocationStart } returns null + every { traceLocationEnd } returns null + } + private val locationEventLowRisk = DefaultContactDiaryLocation( locationId = 456, - locationName = "Jahrestreffen der deutschen SAP Anwendergruppe", - traceLocationID = "12ab-34cd-56ef-78gh-456".decodeBase64() + locationName = checkInLow.locationName, + traceLocationID = checkInLow.traceLocationId ) private val locationEventHighRisk = DefaultContactDiaryLocation( locationId = 457, - locationName = "Kiosk", - traceLocationID = "12ab-34cd-56ef-78gh-457".decodeBase64() + locationName = checkInHigh.locationName, + traceLocationID = checkInHigh.traceLocationId ) private val locationEventLowRiskVisit = DefaultContactDiaryLocationVisit( @@ -108,27 +127,17 @@ open class ContactDiaryOverviewViewModelTest { ) private val traceLocationCheckInRiskLow = object : TraceLocationCheckInRisk { - override val checkInId: Long = 147 + override val checkInId: Long = checkInLow.id override val localDateUtc: LocalDate = date override val riskState: RiskState = RiskState.LOW_RISK } private val traceLocationCheckInRiskHigh = object : TraceLocationCheckInRisk { - override val checkInId: Long = 148 + override val checkInId: Long = checkInHigh.id override val localDateUtc: LocalDate = date override val riskState: RiskState = RiskState.INCREASED_RISK } - private val checkInLow = mockk<CheckIn>().apply { - every { id } returns traceLocationCheckInRiskLow.checkInId - every { description } returns "I can make orange rhyme with banana... Bornana" - } - - private val checkInHigh = mockk<CheckIn>().apply { - every { id } returns traceLocationCheckInRiskHigh.checkInId - every { description } returns "I'm the bad guy cause I caused the high risk" - } - private val aggregatedRiskPerDateResultLowRisk = ExposureWindowDayRisk( dateMillisSinceEpoch = dateMillis, riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCardTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCardTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..9d8c91141da23d079fa53a580d3d6c369d1aaaa0 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/antigen/profile/VCardTest.kt @@ -0,0 +1,133 @@ +package de.rki.coronawarnapp.coronatest.antigen.profile + +import de.rki.coronawarnapp.util.TimeStamper +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.joda.time.Instant +import org.joda.time.format.ISODateTimeFormat +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.BeforeEach +import testhelpers.BaseTest + +class VCardTest : BaseTest() { + + @MockK lateinit var timeStamper: TimeStamper + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { timeStamper.nowUTC } returns Instant.parse("1995-10-31T22:27:10Z") + } + + @Test + fun `Case 1`() { + val profile = RATProfile( + firstName = "Max", + lastName = "Mustermann", + birthDate = ISODateTimeFormat.basicDate().parseLocalDate("19800625"), + street = "Musterstrasse 14", + zipCode = "51466", + city = "Musterstadt", + phone = "0190 1234567", + email = "max@mustermann.de" + ) + VCard(timeStamper).create(profile) shouldBe + """ + BEGIN:VCARD + VERSION:4.0 + N:Mustermann;Max;;; + FN:Max Mustermann + BDAY:19800625 + EMAIL;TYPE=home:max@mustermann.de + TEL;TYPE="cell,home":0190 1234567 + ADR;TYPE=home:;;Musterstrasse 14;Musterstadt;;51466 + REV:19951031T222710Z + END:VCARD + """.trimIndent() + } + + @Test + fun `Case 2`() { + val profile = RATProfile( + firstName = "", + lastName = "", + birthDate = null, + street = "", + zipCode = "", + city = "", + phone = "", + email = "" + ) + VCard(timeStamper).create(profile) shouldBe + """ + BEGIN:VCARD + VERSION:4.0 + N:;;;; + FN: + BDAY: + EMAIL;TYPE=home: + TEL;TYPE="cell,home": + ADR;TYPE=home:;;;;; + REV:19951031T222710Z + END:VCARD + """.trimIndent() + } + + @Test + fun `Case 3`() { + val profile = RATProfile( + firstName = "Max", + lastName = "Mustermann", + birthDate = ISODateTimeFormat.basicDate().parseLocalDate("19800625"), + street = "Mu\\ster;stra,sse 14", + zipCode = "51466", + city = "Musterstadt", + phone = "0190 1234567", + email = "max@mustermann.de" + ) + VCard(timeStamper).create(profile) shouldBe + """ + BEGIN:VCARD + VERSION:4.0 + N:Mustermann;Max;;; + FN:Max Mustermann + BDAY:19800625 + EMAIL;TYPE=home:max@mustermann.de + TEL;TYPE="cell,home":0190 1234567 + ADR;TYPE=home:;;Mu\\ster\;stra\,sse 14;Musterstadt;;51466 + REV:19951031T222710Z + END:VCARD + """.trimIndent() + } + + @Test + fun `Case 4`() { + val profile = RATProfile( + firstName = "Max,", + lastName = "Mustermann;", + birthDate = ISODateTimeFormat.basicDate().parseLocalDate("19800625"), + street = "Mu\\\\ster;stra,sse 14\nA", + zipCode = "51466", + city = "Muster city \n Upper \\county, DC ; US", + phone = "0190 \\1234567", + email = "max@mustermann,;\\.de" + ) + VCard(timeStamper).create(profile) shouldBe + """ + BEGIN:VCARD + VERSION:4.0 + N:Mustermann\;;Max\,;;; + FN:Max\, Mustermann\; + BDAY:19800625 + EMAIL;TYPE=home:max@mustermann\,\;\\.de + TEL;TYPE="cell,home":0190 \\1234567 + ADR;TYPE=home:;;Mu\\\\ster\;stra\,sse 14A;Muster city Upper \\county\, DC \; US;;51466 + REV:19951031T222710Z + END:VCARD + """.trimIndent() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/server/VerificationServerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/server/VerificationServerTest.kt index a7f311ba6a18f1b0107130993b5912513bc0ad1c..86cf613364c9c51cb26871ef000dc051c3c9c9d1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/server/VerificationServerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/server/VerificationServerTest.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.runBlocking import okhttp3.ConnectionSpec import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer +import org.joda.time.Duration import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -211,4 +212,9 @@ class VerificationServerTest : BaseIOTest() { a.headerSizeIgnoringContentLength() shouldBe b.headerSizeIgnoringContentLength() } } + + @Test + fun `test availability constant`() { + VerificationServer.TEST_AVAILABLBILITY shouldBe Duration.standardDays(60) + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensionsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensionsTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..aba14190d8cf6cb2fbc738f41b8a81a361a3d5d2 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/CoronaTestExtensionsTest.kt @@ -0,0 +1,34 @@ +package de.rki.coronawarnapp.coronatest.type + +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import org.joda.time.Duration +import org.joda.time.Instant +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class CoronaTestExtensionsTest : BaseTest() { + + @Test + fun `is test older than 21 days - time changes`() { + val test = mockk<CoronaTest>().apply { + every { registeredAt } returns Instant.EPOCH + } + + test.isOlderThan21Days(Instant.EPOCH.plus(Duration.standardDays(21))) shouldBe false + test.isOlderThan21Days(Instant.EPOCH.plus(Duration.standardDays(22))) shouldBe true + } + + @Test + fun `is test older than 21 days - test changes`() { + val nowUTC = Instant.EPOCH.plus(Duration.standardDays(22)) + mockk<CoronaTest>().apply { + every { registeredAt } returns Instant.EPOCH + }.isOlderThan21Days(nowUTC) shouldBe true + + mockk<CoronaTest>().apply { + every { registeredAt } returns Instant.EPOCH.plus(Duration.standardDays(1)) + }.isOlderThan21Days(nowUTC) shouldBe false + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt index 7a2c51d7dc38bb10a0b02aa99f2ee7b29e9a0060..4401dc012eeb67e776a812430b0211c16283f60d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt @@ -17,6 +17,7 @@ import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN import de.rki.coronawarnapp.coronatest.type.CoronaTestService import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector import de.rki.coronawarnapp.datadonation.analytics.modules.registeredtest.TestResultDataCollector +import de.rki.coronawarnapp.exception.http.BadRequestException import de.rki.coronawarnapp.util.TimeStamper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -93,7 +94,7 @@ class PCRProcessorTest : BaseTest() { instance.pollServer(pcrTest).testResult shouldBe PCR_OR_RAT_PENDING val past60DaysTest = pcrTest.copy( - registeredAt = nowUTC.minus(Duration.standardDays(21)) + registeredAt = nowUTC.minus(Duration.standardDays(61)) ) instance.pollServer(past60DaysTest).testResult shouldBe PCR_REDEEMED @@ -181,4 +182,55 @@ class PCRProcessorTest : BaseTest() { isResultAvailableNotificationSent shouldBe false } } + + @Test + fun `polling is skipped if test is older than 21 days and state was already REDEEMED`() = runBlockingTest { + coEvery { submissionService.asyncRequestTestResult(any()) } answers { PCR_POSITIVE } + + val instance = createInstance() + + val pcrTest = PCRCoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC.minus(Duration.standardDays(22)), + registrationToken = "regtoken", + testResult = PCR_REDEEMED + ) + + // Older than 21 days and already redeemed + instance.pollServer(pcrTest) shouldBe pcrTest + + // Older than 21 days but not in final state, we take value from server + instance.pollServer( + pcrTest.copy(testResult = PCR_NEGATIVE) + ).testResult shouldBe PCR_POSITIVE + } + + @Test + fun `http 400 errors map to REDEEMED (EXPIRED) state after 21 days`() = runBlockingTest { + val ourBadRequest = BadRequestException("Who?") + coEvery { submissionService.asyncRequestTestResult(any()) } throws ourBadRequest + + val instance = createInstance() + + val pcrTest = PCRCoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC, + registrationToken = "regtoken", + testResult = PCR_POSITIVE + ) + + // Test is not older than 21 days, we want the error! + instance.pollServer(pcrTest).apply { + testResult shouldBe PCR_POSITIVE + lastError shouldBe ourBadRequest + } + + // Test IS older than 21 days, we expected the error, and map it to REDEEMED (expired) + instance.pollServer(pcrTest.copy(registeredAt = nowUTC.minus(Duration.standardDays(22)))).apply { + testResult shouldBe PCR_REDEEMED + lastError shouldBe null + } + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt index 76573d88bc5c0dbfe14a727593fa4b37f1e2e55a..b3287063eb1098b7de65538b526782e4c7402725 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt @@ -14,6 +14,7 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.values import de.rki.coronawarnapp.coronatest.type.CoronaTestService +import de.rki.coronawarnapp.exception.http.BadRequestException import de.rki.coronawarnapp.util.TimeStamper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -74,7 +75,7 @@ class RapidAntigenProcessorTest : BaseTest() { instance.pollServer(raTest).testResult shouldBe PCR_OR_RAT_PENDING val past60DaysTest = raTest.copy( - registeredAt = nowUTC.minus(Duration.standardDays(21)) + registeredAt = nowUTC.minus(Duration.standardDays(61)) ) instance.pollServer(past60DaysTest).testResult shouldBe RAT_REDEEMED @@ -146,4 +147,57 @@ class RapidAntigenProcessorTest : BaseTest() { } } } + + @Test + fun `polling is skipped if test is older than 21 days and state was already REDEEMED`() = runBlockingTest { + coEvery { submissionService.asyncRequestTestResult(any()) } answers { RAT_POSITIVE } + + val instance = createInstance() + + val raTest = RACoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC.minus(Duration.standardDays(22)), + registrationToken = "regtoken", + testResult = RAT_REDEEMED, + testedAt = Instant.EPOCH, + ) + + // Older than 21 days and already redeemed + instance.pollServer(raTest) shouldBe raTest + + // Older than 21 days but not in final state, we take value from server + instance.pollServer( + raTest.copy(testResult = RAT_NEGATIVE) + ).testResult shouldBe RAT_POSITIVE + } + + @Test + fun `http 400 errors map to REDEEMED (EXPIRED) state after 21 days`() = runBlockingTest { + val ourBadRequest = BadRequestException("Who?") + coEvery { submissionService.asyncRequestTestResult(any()) } throws ourBadRequest + + val instance = createInstance() + + val raTest = RACoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = nowUTC, + registrationToken = "regtoken", + testResult = RAT_POSITIVE, + testedAt = Instant.EPOCH, + ) + + // Test is not older than 21 days, we want the error! + instance.pollServer(raTest).apply { + testResult shouldBe RAT_POSITIVE + lastError shouldBe ourBadRequest + } + + // Test IS older than 21 days, we expected the error, and map it to REDEEMED (expired) + instance.pollServer(raTest.copy(registeredAt = nowUTC.minus(Duration.standardDays(22)))).apply { + testResult shouldBe RAT_REDEEMED + lastError shouldBe null + } + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt index 3ad5c90bc8cf3913cd7ca78cc6faa48373a810c5..55891e4f37e1236dd8fbd4baaf8e2db4b115f575 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt @@ -53,7 +53,7 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { private val coronaTests: MutableStateFlow<Set<CoronaTest>> = MutableStateFlow( setOf( - mockk<CoronaTest>().apply { every { isSubmissionAllowed } returns false } + mockk<CoronaTest>().apply { every { isPositive } returns false } ) ) @@ -241,7 +241,7 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { @Test fun `we do not submit keys if user got positive test results`() = runBlockingTest { coronaTests.value = setOf( - mockk<CoronaTest>().apply { every { isSubmissionAllowed } returns true } + mockk<CoronaTest>().apply { every { isPositive } returns true } ) createInstance().run(DownloadDiagnosisKeysTask.Arguments()) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreatorTest.kt index d66bb45ec8852bed62d248b9d19a77d3ee5c4c09..000eebe6ce6d0be416bfba37367ad391df786f34 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreatorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/checkout/ContactJournalCheckInEntryCreatorTest.kt @@ -4,8 +4,8 @@ import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.presencetracing.checkins.common.locationName import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc -import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.coEvery @@ -21,7 +21,6 @@ import okio.ByteString.Companion.encode import org.joda.time.Days import org.joda.time.Instant import org.joda.time.Minutes -import org.joda.time.format.DateTimeFormat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest @@ -50,7 +49,7 @@ class ContactJournalCheckInEntryCreatorTest : BaseTest() { private val testLocation = DefaultContactDiaryLocation( locationId = 123L, - locationName = "${testCheckIn.description}, ${testCheckIn.address}, ${testCheckIn.traceLocationStart?.toPrettyDate()} - ${testCheckIn.traceLocationEnd?.toPrettyDate()}", + locationName = testCheckIn.locationName, traceLocationID = testCheckIn.traceLocationId ) @@ -62,8 +61,6 @@ class ContactJournalCheckInEntryCreatorTest : BaseTest() { duration = Minutes.minutes(60).toStandardDuration() ) - private fun Instant.toPrettyDate(): String = toUserTimeZone().toString(DateTimeFormat.shortDateTime()) - @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -111,28 +108,6 @@ class ContactJournalCheckInEntryCreatorTest : BaseTest() { } } - @Test - fun `Location name concatenates description, address and if both are set trace location start and end date`() { - val testCheckInNoTraceLocationStartDate = testCheckIn.copy(traceLocationStart = null) - val testCheckInNoTraceLocationEndDate = testCheckIn.copy(traceLocationEnd = null) - val testCheckInNoTraceLocationStartAndEndDate = - testCheckIn.copy(traceLocationStart = null, traceLocationEnd = null) - - createInstance().apply { - testCheckIn.validateLocationName(testCheckIn.toLocationName()) - testCheckInNoTraceLocationStartDate.validateLocationName(testCheckInNoTraceLocationStartDate.toLocationName()) - testCheckInNoTraceLocationEndDate.validateLocationName(testCheckInNoTraceLocationEndDate.toLocationName()) - testCheckInNoTraceLocationStartAndEndDate.validateLocationName(testCheckInNoTraceLocationStartAndEndDate.toLocationName()) - } - } - - private fun CheckIn.validateLocationName(nameToValidate: String) { - nameToValidate shouldBe when (traceLocationStart != null && traceLocationEnd != null) { - true -> "$description, $address, ${traceLocationStart?.toPrettyDate()} - ${traceLocationEnd?.toPrettyDate()}" - else -> "$description, $address" - } - } - @Test fun `CheckIn to ContactDiaryLocationVisit is correct`() { createInstance().apply { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtensionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtensionTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..8851229725e68370b9e06852532580e5431a8df5 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/checkins/common/CheckInExtensionTest.kt @@ -0,0 +1,54 @@ +package de.rki.coronawarnapp.presencetracing.checkins.common + +import de.rki.coronawarnapp.presencetracing.checkins.CheckIn +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeBase64 +import okio.ByteString.Companion.encode +import org.joda.time.Instant +import org.joda.time.format.DateTimeFormat +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class CheckInExtensionTest : BaseTest() { + + private val testCheckIn = CheckIn( + id = 42L, + traceLocationId = "traceLocationId1".decodeBase64()!!, + version = 1, + type = 1, + description = "Restaurant", + address = "Around the corner", + traceLocationStart = Instant.parse("2021-03-04T22:00+01:00"), + traceLocationEnd = Instant.parse("2021-03-04T23:00+01:00"), + defaultCheckInLengthInMinutes = null, + cryptographicSeed = "cryptographicSeed".encode(), + cnPublicKey = "cnPublicKey", + checkInStart = Instant.parse("2021-03-04T22:00+01:00"), + checkInEnd = Instant.parse("2021-03-04T23:00+01:00"), + completed = false, + createJournalEntry = true + ) + + private fun Instant.toPrettyDate(): String = toUserTimeZone().toString(DateTimeFormat.shortDateTime()) + + @Test + fun `Location name concatenates description, address and if both are set trace location start and end date`() { + val testCheckInNoTraceLocationStartDate = testCheckIn.copy(traceLocationStart = null) + val testCheckInNoTraceLocationEndDate = testCheckIn.copy(traceLocationEnd = null) + val testCheckInNoTraceLocationStartAndEndDate = + testCheckIn.copy(traceLocationStart = null, traceLocationEnd = null) + + testCheckIn.validateLocationName() + testCheckInNoTraceLocationStartDate.validateLocationName() + testCheckInNoTraceLocationEndDate.validateLocationName() + testCheckInNoTraceLocationStartAndEndDate.validateLocationName() + } + + private fun CheckIn.validateLocationName() { + locationName shouldBe when (traceLocationStart != null && traceLocationEnd != null) { + true -> "$description, $address, ${traceLocationStart?.toPrettyDate()} - ${traceLocationEnd?.toPrettyDate()}" + else -> "$description, $address" + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTaskTest.kt index 57b4cfb409c03ec328048fc8b45bf0824cc33c0e..98d7ace516554e1496faa5d6e0c784999f0db1c4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTaskTest.kt @@ -1,6 +1,9 @@ package de.rki.coronawarnapp.presencetracing.risk.execution +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository +import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut import de.rki.coronawarnapp.presencetracing.risk.calculation.CheckInWarningMatcher import de.rki.coronawarnapp.presencetracing.risk.calculation.PresenceTracingRiskMapper import de.rki.coronawarnapp.presencetracing.risk.calculation.createCheckIn @@ -24,6 +27,7 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runBlockingTest import org.joda.time.Duration @@ -42,11 +46,21 @@ class PresenceTracingWarningTaskTest : BaseTest() { @MockK lateinit var traceWarningRepository: TraceWarningRepository @MockK lateinit var checkInsRepository: CheckInRepository @MockK lateinit var presenceTracingRiskMapper: PresenceTracingRiskMapper + @MockK lateinit var autoCheckOut: AutoCheckOut + @MockK lateinit var coronaTestRepository: CoronaTestRepository + + private val coronaTests: MutableStateFlow<Set<CoronaTest>> = MutableStateFlow( + setOf( + mockk<CoronaTest>().apply { every { isPositive } returns false } + ) + ) @BeforeEach fun setup() { MockKAnnotations.init(this) + every { coronaTestRepository.coronaTests } returns coronaTests + every { timeStamper.nowUTC } returns Instant.ofEpochMilli(9000) coEvery { syncTool.syncPackages() } returns TraceWarningPackageSyncTool.SyncResult(successful = true) coEvery { checkInWarningMatcher.process(any(), any()) } answers { @@ -75,6 +89,9 @@ class PresenceTracingWarningTaskTest : BaseTest() { } coEvery { presenceTracingRiskMapper.clearConfig() } just Runs + + coEvery { autoCheckOut.processOverDueCheckouts() } returns emptyList() + coEvery { autoCheckOut.refreshAlarm() } returns true } private fun createInstance() = PresenceTracingWarningTask( @@ -84,7 +101,9 @@ class PresenceTracingWarningTaskTest : BaseTest() { presenceTracingRiskRepository = presenceTracingRiskRepository, traceWarningRepository = traceWarningRepository, checkInsRepository = checkInsRepository, - presenceTracingRiskMapper = presenceTracingRiskMapper + presenceTracingRiskMapper = presenceTracingRiskMapper, + coronaTestRepository = coronaTestRepository, + autoCheckOut = autoCheckOut, ) @Test @@ -92,9 +111,15 @@ class PresenceTracingWarningTaskTest : BaseTest() { createInstance().run(mockk()) shouldNotBe null coVerifySequence { + autoCheckOut.processOverDueCheckouts() + autoCheckOut.refreshAlarm() + syncTool.syncPackages() presenceTracingRiskRepository.deleteStaleData() checkInsRepository.checkInsWithinRetention + + coronaTestRepository.coronaTests + traceWarningRepository.unprocessedWarningPackages checkInWarningMatcher.process(any(), any()) @@ -229,6 +254,35 @@ class PresenceTracingWarningTaskTest : BaseTest() { PresenceTracingWarningTask.Config().executionTimeout shouldBeLessThan maxDuration } + @Test + fun `we do not submit keys if user got positive test results`() = runBlockingTest { + coronaTests.value = setOf( + mockk<CoronaTest>().apply { every { isPositive } returns true } + ) + + createInstance().run(mockk()) shouldNotBe null + + coVerifySequence { + syncTool.syncPackages() + presenceTracingRiskRepository.deleteStaleData() + checkInsRepository.checkInsWithinRetention + + coronaTestRepository.coronaTests + } + + coVerify(exactly = 0) { + traceWarningRepository.unprocessedWarningPackages + + checkInWarningMatcher.process(any(), any()) + + presenceTracingRiskRepository.reportCalculation( + successful = any(), + overlaps = any() + ) + traceWarningRepository.markPackagesProcessed(any()) + } + } + companion object { val CHECKIN_1 = createCheckIn( id = 2L, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt index 77e65bd863c06f9f0ae662767e22adf81cd63c3a..7f3f3be61ad7e40a5e30f25ed2d2388406e3dcf6 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt @@ -1,9 +1,5 @@ package de.rki.coronawarnapp.risk -import android.content.Context -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.coronatest.CoronaTestRepository @@ -13,7 +9,6 @@ import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKey import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKeyInfo import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.nearby.ENFClient -import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.task.Task @@ -43,7 +38,6 @@ import testhelpers.BaseTest class RiskLevelTaskTest : BaseTest() { @MockK lateinit var riskLevels: RiskLevels - @MockK lateinit var context: Context @MockK lateinit var enfClient: ENFClient @MockK lateinit var timeStamper: TimeStamper @MockK lateinit var backgroundModeStatus: BackgroundModeStatus @@ -54,7 +48,6 @@ class RiskLevelTaskTest : BaseTest() { @MockK lateinit var keyCacheRepository: KeyCacheRepository @MockK lateinit var coronaTestRepository: CoronaTestRepository @MockK lateinit var analyticsExposureWindowCollector: AnalyticsExposureWindowCollector - @MockK lateinit var autoCheckOut: AutoCheckOut private val arguments: Task.Arguments = object : Task.Arguments {} @@ -67,42 +60,54 @@ class RiskLevelTaskTest : BaseTest() { ) ) + private val testTimeNow = Instant.parse("2020-12-28") + private val testAggregatedResult = mockk<EwAggregatedRiskResult>().apply { + every { isIncreasedRisk() } returns true + } + private val testCachedKey = mockk<CachedKey>().apply { + every { info } returns mockk<CachedKeyInfo>().apply { + every { toDateTime() } returns testTimeNow.toDateTime().minusDays(1) + } + } + @BeforeEach fun setup() { MockKAnnotations.init(this) mockkObject(TimeVariables) + // Mocks setup a happy path, i.e. successful calculation + + every { timeStamper.nowUTC } returns testTimeNow + every { coronaTestRepository.coronaTests } returns coronaTests - every { configData.isDeviceTimeCorrect } returns true every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(true) coEvery { appConfigProvider.getAppConfig() } returns configData - every { configData.identifier } returns "config-identifier" - every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns mockk<ConnectivityManager>().apply { - every { activeNetwork } returns mockk<Network>().apply { - every { getNetworkCapabilities(any()) } returns mockk<NetworkCapabilities>().apply { - every { hasCapability(any()) } returns true - } - } + configData.apply { + every { identifier } returns "config-identifier" + every { isDeviceTimeCorrect } returns true } - every { enfClient.isTracingEnabled } returns flowOf(true) - every { timeStamper.nowUTC } returns Instant.EPOCH - every { riskLevelSettings.lastUsedConfigIdentifier = any() } just Runs - coEvery { keyCacheRepository.getAllCachedKeys() } returns emptyList() - coEvery { riskLevelStorage.storeResult(any()) } just Runs - coEvery { autoCheckOut.processOverDueCheckouts() } returns emptyList() - coEvery { autoCheckOut.refreshAlarm() } returns true + coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf(testCachedKey) + enfClient.apply { + every { isTracingEnabled } returns flowOf(true) + coEvery { exposureWindows() } returns listOf() + } + + riskLevels.apply { + every { calculateRisk(any(), any()) } returns null + every { aggregateResults(any(), any()) } returns testAggregatedResult + } + coEvery { analyticsExposureWindowCollector.reportRiskResultsPerWindow(any()) } just Runs } private fun createTask() = RiskLevelTask( riskLevels = riskLevels, - context = context, enfClient = enfClient, timeStamper = timeStamper, backgroundModeStatus = backgroundModeStatus, @@ -112,7 +117,6 @@ class RiskLevelTaskTest : BaseTest() { keyCacheRepository = keyCacheRepository, coronaTestRepository = coronaTestRepository, analyticsExposureWindowCollector = analyticsExposureWindowCollector, - autoCheckOut = autoCheckOut ) @Test @@ -131,33 +135,17 @@ class RiskLevelTaskTest : BaseTest() { every { configData.localOffset } returns Duration.standardHours(5) createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = Instant.EPOCH, + calculatedAt = testTimeNow, failureReason = EwRiskLevelResult.FailureReason.INCORRECT_DEVICE_TIME ) } - @Test - fun `risk calculation is skipped if internet is unavailable`() = runBlockingTest { - every { context.getSystemService(Context.CONNECTIVITY_SERVICE) } returns mockk<ConnectivityManager>().apply { - every { activeNetwork } returns mockk<Network>().apply { - every { getNetworkCapabilities(any()) } returns mockk<NetworkCapabilities>().apply { - every { hasCapability(any()) } returns false - } - } - } - - createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = Instant.EPOCH, - failureReason = EwRiskLevelResult.FailureReason.NO_INTERNET - ) - } - @Test fun `risk calculation is skipped if tracing is disabled`() = runBlockingTest { every { enfClient.isTracingEnabled } returns flowOf(false) createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = Instant.EPOCH, + calculatedAt = testTimeNow, failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF ) } @@ -167,7 +155,7 @@ class RiskLevelTaskTest : BaseTest() { coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf() every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(true) createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = Instant.EPOCH, + calculatedAt = testTimeNow, failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS ) } @@ -177,7 +165,7 @@ class RiskLevelTaskTest : BaseTest() { coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf() every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(false) createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = Instant.EPOCH, + calculatedAt = testTimeNow, failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS_MANUAL ) } @@ -282,27 +270,10 @@ class RiskLevelTaskTest : BaseTest() { @Test fun `risk calculation returns aggregated risk result`() = runBlockingTest { - val cachedKey = mockk<CachedKey>().apply { - every { info } returns mockk<CachedKeyInfo>().apply { - every { toDateTime() } returns DateTime.parse("2020-12-28").minusDays(1) - } - } - val now = Instant.parse("2020-12-28") - val aggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply { - every { isIncreasedRisk() } returns true - } - - coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf(cachedKey) - coEvery { enfClient.exposureWindows() } returns listOf() - every { riskLevels.calculateRisk(any(), any()) } returns null - every { riskLevels.aggregateResults(any(), any()) } returns aggregatedRiskResult - every { timeStamper.nowUTC } returns now - coEvery { analyticsExposureWindowCollector.reportRiskResultsPerWindow(any()) } just Runs - createTask().run(arguments) shouldBe EwRiskLevelTaskResult( - calculatedAt = now, + calculatedAt = testTimeNow, failureReason = null, - ewAggregatedRiskResult = aggregatedRiskResult, + ewAggregatedRiskResult = testAggregatedResult, listOf() ) } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt index 4d4c07cbafcb1e682049f28e99e49d875d106588..9db51d8a72e1bc6963746d1fc014be953b9eb4af 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt @@ -7,6 +7,7 @@ import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResul import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.Runs +import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just @@ -35,7 +36,7 @@ class SubmissionTestResultPendingViewModelTest : BaseTest() { submissionRepository.apply { every { testForType(any()) } returns testFlow - every { setViewedTestResult(any()) } just Runs + coEvery { setViewedTestResult(any()) } just Runs } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt index c3f66c812d1ff2ba55f9ab120f21185b31435381..41eb4280816b12a78fe0c6ba1e830ae58188aeb9 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt @@ -45,7 +45,7 @@ class SubmissionConsentViewModelTest { fun setUp() { MockKAnnotations.init(this) every { interoperabilityRepository.countryList } returns MutableStateFlow(countryList) - every { submissionRepository.giveConsentToSubmission(any()) } just Runs + coEvery { submissionRepository.giveConsentToSubmission(any()) } just Runs coEvery { qrCodeRegistrationStateProcessor.showRedeemedTokenWarning } returns SingleLiveEvent() coEvery { qrCodeRegistrationStateProcessor.registrationState } returns MutableLiveData( QrCodeRegistrationStateProcessor.RegistrationState(ApiRequestState.IDLE) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt index bd8f052d20fbc544f19b3ef378a2567a6c378c9e..846984526b98bb72db9be53e8dbd89d777a543b7 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt @@ -1,7 +1,6 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan import androidx.lifecycle.MutableLiveData -import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQrCodeValidator import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException @@ -87,12 +86,9 @@ class SubmissionQRCodeScanViewModelTest : BaseTest() { viewModel.qrCodeValidationState.value shouldBe ValidationState.STARTED - QRCodeCensor.lastGUID = null - viewModel.onQrCodeAvailable(validQrCode) viewModel.qrCodeValidationState.observeForever {} viewModel.qrCodeValidationState.value shouldBe ValidationState.SUCCESS - QRCodeCensor.lastGUID = guid // invalid guid viewModel.onQrCodeAvailable(invalidQrCode) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt index fad77e25164561a6928eb11a15575266f5f2804b..163c05530dcae7e07429b90c5fe66c0a88419128 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt @@ -14,6 +14,7 @@ import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAv import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.Runs +import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just @@ -55,7 +56,7 @@ class SubmissionTestResultAvailableViewModelTest : BaseTest() { // TODO Check specific behavior submissionRepository.apply { - every { refreshTest(any()) } just Runs + coEvery { refreshTest(any()) } just Runs every { testForType(type = any()) } returns coronaTestFlow } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt index 3e16e938556fca6f03e13e043987cc0dffff9dcc..a69425f454903005cd4e9d07ab0b6742cfc58157 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt @@ -13,6 +13,8 @@ import de.rki.coronawarnapp.submission.auto.AutoSubmission import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryUpdater import io.mockk.MockKAnnotations import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just @@ -57,7 +59,7 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModelTest : BaseTest() { every { interoperabilityRepository.countryList } returns emptyFlow() submissionRepository.apply { - every { giveConsentToSubmission(any()) } just Runs + coEvery { giveConsentToSubmission(any()) } just Runs every { testForType(any()) } returns coronaTestFlow } @@ -87,7 +89,7 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModelTest : BaseTest() { viewModel.onConsentButtonClicked() - verify { submissionRepository.giveConsentToSubmission(any()) } + coVerify { submissionRepository.giveConsentToSubmission(any()) } verify { tekHistoryUpdater.updateTEKHistoryOrRequestPermission() } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt index 5a81ac156199fde1b94883142a459884d1cb0791..df449ddd86a9903b40a22f84c9e97e03bcf7d848 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt @@ -7,11 +7,12 @@ import de.rki.coronawarnapp.ui.Country import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk -import io.mockk.verify import kotlinx.coroutines.flow.MutableStateFlow import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -41,8 +42,8 @@ class SubmissionYourConsentViewModelTest : BaseTest() { MockKAnnotations.init(this) every { submissionRepository.testForType(any()) } returns coronaTestFlow every { interoperabilityRepository.countryList } returns MutableStateFlow(countryList) - every { submissionRepository.giveConsentToSubmission(any()) } just Runs - every { submissionRepository.revokeConsentToSubmission(any()) } just Runs + coEvery { submissionRepository.giveConsentToSubmission(any()) } just Runs + coEvery { submissionRepository.revokeConsentToSubmission(any()) } just Runs } private fun createViewModel(): SubmissionYourConsentViewModel = SubmissionYourConsentViewModel( @@ -75,7 +76,7 @@ class SubmissionYourConsentViewModelTest : BaseTest() { } createViewModel().switchConsent() - verify(exactly = 1) { submissionRepository.revokeConsentToSubmission(any()) } + coVerify(exactly = 1) { submissionRepository.revokeConsentToSubmission(any()) } } @Test @@ -85,7 +86,7 @@ class SubmissionYourConsentViewModelTest : BaseTest() { } createViewModel().switchConsent() - verify(exactly = 1) { submissionRepository.giveConsentToSubmission(any()) } + coVerify(exactly = 1) { submissionRepository.giveConsentToSubmission(any()) } } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt index b78a603a9f5ecca262d3ec469086c2a4848f998a..eca8c03f5bf3cd88a228bdf9a7376eacb890f274 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt @@ -6,16 +6,17 @@ import de.rki.coronawarnapp.bugreporting.BugReportingSettings import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings import de.rki.coronawarnapp.datadonation.analytics.Analytics import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings import de.rki.coronawarnapp.datadonation.survey.SurveySettings import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository +import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.presencetracing.TraceLocationSettings import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository -import de.rki.coronawarnapp.main.CWASettings -import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.presencetracing.warning.storage.TraceWarningRepository import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.statistics.source.StatisticsProvider @@ -58,6 +59,7 @@ internal class DataResetTest : BaseTest() { @MockK lateinit var checkInRepository: CheckInRepository @MockK lateinit var traceLocationSettings: TraceLocationSettings @MockK lateinit var coronaTestRepository: CoronaTestRepository + @MockK lateinit var ratProfileSettings: RATProfileSettings @BeforeEach fun setUp() { @@ -88,7 +90,8 @@ internal class DataResetTest : BaseTest() { checkInRepository = checkInRepository, traceLocationSettings = traceLocationSettings, traceWarningRepository = traceWarningRepository, - coronaTestRepository = coronaTestRepository + coronaTestRepository = coronaTestRepository, + ratProfileSettings = ratProfileSettings ) @Test @@ -122,5 +125,6 @@ internal class DataResetTest : BaseTest() { coVerify(exactly = 1) { traceLocationRepository.deleteAllTraceLocations() } coVerify(exactly = 1) { checkInRepository.clear() } coVerify(exactly = 1) { coronaTestRepository.clear() } + coVerify(exactly = 1) { ratProfileSettings.deleteProfile() } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/network/NetworkStateProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/network/NetworkStateProviderTest.kt index 6c48eeb1e36f26ec1d433f14793a454ad5382cae..104e1d6e9b5ead4e8201798cccf725b94fdd3499 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/network/NetworkStateProviderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/network/NetworkStateProviderTest.kt @@ -198,7 +198,7 @@ class NetworkStateProviderTest : BaseTest() { } @Test - fun `metered connection state can be overriden via test settings`() = runBlockingTest2(ignoreActive = true) { + fun `metered connection state can be overridden via test settings`() = runBlockingTest2(ignoreActive = true) { every { testSettings.fakeMeteredConnection } returns mockFlowPreference(true) val instance = createInstance(this) @@ -237,4 +237,33 @@ class NetworkStateProviderTest : BaseTest() { linkProperties = null ).isMeteredConnection shouldBe true } + + @Test + fun `if we fail to register the callback, we do not attempt to unregister it`() = + runBlockingTest2(ignoreActive = true) { + every { + conMan.registerNetworkCallback( + any(), + any<ConnectivityManager.NetworkCallback>() + ) + } throws SecurityException() + + val instance = createInstance(this) + + instance.networkState.first() shouldBe NetworkStateProvider.State( + activeNetwork = null, + capabilities = null, + linkProperties = null + ) + + advanceUntilIdle() + + verifySequence { + conMan.activeNetwork + conMan.getNetworkCapabilities(network) + conMan.getLinkProperties(network) + conMan.registerNetworkCallback(networkRequest, any<ConnectivityManager.NetworkCallback>()) + } + verify(exactly = 0) { conMan.unregisterNetworkCallback(any<ConnectivityManager.NetworkCallback>()) } + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt index c932ef5a240341b9ec60177bdaa0b1c46bcc7499..c354d0634b62b0778ba9575daec973705b3db689 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt @@ -9,7 +9,6 @@ class BackgroundConstantsTest { fun allBackgroundConstants() { Assert.assertEquals(BackgroundConstants.KIND_DELAY, 1L) Assert.assertEquals(BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD, 2) - Assert.assertEquals(BackgroundConstants.POLLING_VALIDITY_MAX_DAYS, 21) Assert.assertEquals(BackgroundConstants.BACKOFF_INITIAL_DELAY, 8L) } } diff --git a/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt similarity index 73% rename from Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt rename to Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt index 1041e72dd00afe48bdd692be583af5696925023d..80af61128b11a0964ffbe0261df4655fbe7d597f 100644 --- a/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt +++ b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt @@ -1,9 +1,25 @@ -package testhelpers.extensions +/* + * Copyright (C) 2019 The Android Open Source Project + * + * 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. + */ /** * Thanks to https://github.com/android/architecture-components-samples/blob/master/LiveDataSample/app/src/test/java/com/android/example/livedatabuilder/util/LiveDataTestUtil.kt */ +package testhelpers.extensions + import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import java.util.concurrent.CountDownLatch diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt index bc5ae9e45a58d362c2de5a11464488638ac300c2..c1b802516a2f5aa590dd3cf5ba6fcbd6d23e5906 100644 --- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt @@ -1,25 +1,19 @@ package de.rki.coronawarnapp.test.debugoptions.ui -import androidx.lifecycle.Observer import de.rki.coronawarnapp.environment.EnvironmentSetup import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations -import io.mockk.Runs import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.just -import io.mockk.mockk -import io.mockk.verify import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import testhelpers.BaseTestInstrumentation import testhelpers.TestDispatcherProvider -import testhelpers.extensions.CoroutinesTestExtension import testhelpers.extensions.InstantExecutorExtension -import testhelpers.flakyTest +import testhelpers.extensions.getOrAwaitValue -@ExtendWith(InstantExecutorExtension::class, CoroutinesTestExtension::class) +@ExtendWith(InstantExecutorExtension::class) class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() { @MockK private lateinit var environmentSetup: EnvironmentSetup @@ -54,38 +48,15 @@ class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() { ) @Test - fun `toggeling the env works`() = flakyTest { + fun `toggeling the env works`() { currentEnvironment = EnvironmentSetup.Type.DEV val vm = createViewModel() - - val states = mutableListOf<EnvironmentState>() - val observerState = mockk<Observer<EnvironmentState>>() - every { observerState.onChanged(capture(states)) } just Runs - vm.environmentState.observeForever(observerState) - - val events = mutableListOf<EnvironmentSetup.Type>() - val observerEvent = mockk<Observer<EnvironmentSetup.Type>>() - every { observerEvent.onChanged(capture(events)) } just Runs - vm.environmentChangeEvent.observeForever(observerEvent) + vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.DEV vm.selectEnvironmentTytpe(EnvironmentSetup.Type.DEV.rawKey) - vm.selectEnvironmentTytpe(EnvironmentSetup.Type.WRU_XA.rawKey) - - verify(exactly = 3, timeout = 3000) { observerState.onChanged(any()) } - verify(exactly = 2, timeout = 3000) { observerEvent.onChanged(any()) } - - states[0].apply { - current shouldBe EnvironmentSetup.Type.DEV - } - - states[1].apply { - current shouldBe EnvironmentSetup.Type.DEV - } - events[0] shouldBe EnvironmentSetup.Type.DEV + vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.DEV - states[2].apply { - current shouldBe EnvironmentSetup.Type.WRU_XA - } - events[1] shouldBe EnvironmentSetup.Type.WRU_XA + vm.selectEnvironmentTytpe(EnvironmentSetup.Type.WRU_XA.rawKey) + vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.WRU_XA } } diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 7a118b49be750543e59f7b9c55123e11322b00c6..0000000000000000000000000000000000000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source "https://rubygems.org" - -gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index b8511d468096a4e8182ec963b7209cb912ca7165..0000000000000000000000000000000000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,201 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.3) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - artifactory (3.0.15) - atomos (0.1.3) - aws-eventstream (1.1.0) - aws-partitions (1.422.0) - aws-sdk-core (3.111.2) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.41.0) - aws-sdk-core (~> 3, >= 3.109.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.87.0) - aws-sdk-core (~> 3, >= 3.109.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.2) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - claide (1.0.3) - colored (1.2) - colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - declarative (0.0.20) - declarative-option (0.1.0) - digest-crc (0.6.3) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.2.1) - excon (0.78.1) - faraday (1.3.0) - faraday-net_http (~> 1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-net_http (1.0.1) - faraday_middleware (1.0.0) - faraday (~> 1.0) - fastimage (2.2.1) - fastlane (2.172.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.37.0, < 0.39.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - gh_inspector (1.1.3) - google-api-client (0.38.0) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-apis-core (0.2.1) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.14) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - rexml - signet (~> 0.14) - webrick - google-apis-iamcredentials_v1 (0.1.0) - google-apis-core (~> 0.1) - google-apis-storage_v1 (0.1.0) - google-apis-core (~> 0.1) - google-cloud-core (1.5.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.4.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.0.1) - google-cloud-storage (1.30.0) - addressable (~> 2.5) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) - mini_mime (~> 1.0) - googleauth (0.15.0) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (~> 0.14) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) - httpclient (2.8.3) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.2) - memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.0.2) - multi_json (1.15.0) - multipart-post (2.0.0) - nanaimo (0.3.0) - naturally (2.2.1) - os (1.1.1) - plist (3.6.0) - public_suffix (4.0.6) - rake (13.0.3) - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.2.4) - rouge (2.0.7) - ruby2_keywords (0.0.4) - rubyzip (2.3.0) - security (0.1.3) - signet (0.14.1) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.8) - CFPropertyList - naturally - slack-notifier (2.3.2) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - tty-cursor (0.7.1) - tty-screen (0.8.1) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) - webrick (1.7.0) - word_wrap (1.0.0) - xcodeproj (1.19.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - ruby - -DEPENDENCIES - fastlane - -BUNDLED WITH - 2.2.4 diff --git a/fastlane/Screengrabfile b/fastlane/Screengrabfile deleted file mode 100644 index d914a83a9879e904443a5b827e01c809330c07cf..0000000000000000000000000000000000000000 --- a/fastlane/Screengrabfile +++ /dev/null @@ -1,8 +0,0 @@ -locales ['de-DE', 'en-US'] -use_adb_root true -clear_previous_screenshots true -app_package_name 'de.rki.coronawarnapp.test' -launch_arguments ["annotation testhelpers.Screenshot"] -test_instrumentation_runner 'testhelpers.TestApplicationUIRunner' -app_apk_path Dir.glob('**/build/outputs/apk/deviceForTesters/debug/*.apk').first -tests_apk_path Dir.glob('**/build/outputs/apk/androidTest/deviceForTesters/debug/*.apk').first \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index f050c033923f4b2a8b12287e6db84bfabf4ff347..ec9858da2ec70c1b5288fe854f3de5abc888d0d2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,4 @@ org.gradle.dependency.verification.console=verbose VERSION_MAJOR=2 VERSION_MINOR=3 VERSION_PATCH=0 -VERSION_BUILD=0 +VERSION_BUILD=5 diff --git a/screenshot.sh b/screenshot.sh deleted file mode 100644 index 2a9c13d5767acd89e3f45427d674be714d72b631..0000000000000000000000000000000000000000 --- a/screenshot.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/zsh -bundle exec fastlane screengrab -fastlane run zip output_path:"fastlane/metadata/screenshots.zip" path:"fastlane/metadata/android"