diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index f85ac0b82..324f92c4c 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -39,7 +39,7 @@ jobs:
with:
java-version: 1.8
- name: Run tests
- uses: reactivecircus/android-emulator-runner@v2
+ uses: reactivecircus/android-emulator-runner@5de26e4bd23bf523e8a4b7f077df8bfb8e52b50e
with:
api-level: 29
script: ./gradlew connectedDevDebugAndroidTest
diff --git a/.github/workflows/appcenter_abnahme.yml b/.github/workflows/appcenter_abnahme.yml
index 83fa2e9a7..ea6ee6bd2 100644
--- a/.github/workflows/appcenter_abnahme.yml
+++ b/.github/workflows/appcenter_abnahme.yml
@@ -18,7 +18,7 @@ jobs:
with:
gradle-cmd: assembleAbnahmeRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
- uses: wzieba/AppCenter-Github-Action@v1.0.0
+ uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_APP_ABNAHME}}
token: ${{secrets.APPCENTER_API_TOKEN}}
diff --git a/.github/workflows/appcenter_dev.yml b/.github/workflows/appcenter_dev.yml
index 162892a6a..a6d89f1fa 100644
--- a/.github/workflows/appcenter_dev.yml
+++ b/.github/workflows/appcenter_dev.yml
@@ -18,7 +18,7 @@ jobs:
with:
gradle-cmd: assembleDevRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
- uses: wzieba/AppCenter-Github-Action@v1.0.0
+ uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_APP_DEV}}
token: ${{secrets.APPCENTER_API_TOKEN}}
diff --git a/.github/workflows/appcenter_prod.yml b/.github/workflows/appcenter_prod.yml
index 6e51b29f6..c3d24ac1b 100644
--- a/.github/workflows/appcenter_prod.yml
+++ b/.github/workflows/appcenter_prod.yml
@@ -18,7 +18,7 @@ jobs:
with:
gradle-cmd: assembleProdRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- name: upload artefact to App Center
- uses: wzieba/AppCenter-Github-Action@v1.0.0
+ uses: wzieba/AppCenter-Github-Action@8db6b765c4d7ce337bd783ea986f17ce0c9a9e85
with:
appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_APP}}
token: ${{secrets.APPCENTER_API_TOKEN}}
diff --git a/README.md b/README.md
index c7a986f34..6a1b27eec 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,11 @@
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://github.com/SwissCovid/swisscovid-app-android/blob/master/LICENSE)
![Android Build](https://github.com/SwissCovid/swisscovid-app-android/workflows/Android%20Build/badge.svg)
-SwissCovid is the official contact tracing app of Switzerland. The app can be installed from the [Google Play Store](https://play.google.com/store/apps/details?id=ch.admin.bag.dp3t). The SwissCovid 2.0 app uses two types of contact tracing to prevent the spread of COVID-19.
+SwissCovid is the official contact tracing app of Switzerland. The app can be installed from the [Google Play Store](https://play.google.com/store/apps/details?id=ch.admin.bag.dp3t). The app design, UX and implementation was done by [Ubique](https://www.ubique.ch/?app=github).
+
+## Contact tracing
+
+The SwissCovid 2.0 app uses two types of contact tracing to prevent the spread of COVID-19.
With proximity tracing close contacts are detected using the bluetooth technology. For this the [DP3T Android SDK](https://github.com/DP-3T/dp3t-sdk-android) is used that builds on top of the Google & Apple Exposure Notifications. This feature is called SwissCovid encounters.
diff --git a/app/build.gradle b/app/build.gradle
index d9819ed34..2b50b20ed 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -24,8 +24,8 @@ android {
applicationId "ch.admin.bag.dp3t"
minSdkVersion 23
targetSdkVersion 30
- versionCode 20010
- versionName "2.0.1"
+ versionCode 21000
+ versionName "2.1.0"
resConfigs "en", "fr", "de", "it", "pt", "es", "sq", "bs", "hr", "sr", "rm", "tr", "ti"
buildConfigField "long", "BUILD_TIME", readPropertyWithDefault('buildTimestamp', System.currentTimeMillis()) + 'L'
diff --git a/app/src/main/assets/disclaimer/de/terms_of_use.html b/app/src/main/assets/disclaimer/de/terms_of_use.html
index 287fc2e41..41715359e 100644
--- a/app/src/main/assets/disclaimer/de/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/de/terms_of_use.html
@@ -17,7 +17,7 @@
1.2
- Die App des Bundesamtes für Gesundheit (BAG) stützt sich auf das Epidemiengesetz vom 28. September 2012 (EpG; SR 818.101), die Verordnung vom 24. Juni 2020 über das Proximity-Tracing-System für das Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25), dem Bundesgesetz über die gesetzlichen Grundlagen für Verordnungen des Bundesrates zur Bewältigung der Covid-19-Epidemie vom 25. September 2020 (Covid-19-Gesetz; SR 818.102) sowie der Verordnung vom 30. Juni 2021 über ein System zur Benachrichtigung über eine mögliche Ansteckung mit dem Coronavirus Sars-CoV-2 an Veranstaltungen (VBV; SR 818.102.4).
+ Die App des Bundesamtes für Gesundheit (BAG) stützt sich auf das Epidemiengesetz vom 28. September 2012 (EpG; SR 818.101), die Verordnung vom 24. Juni 2020 über das Proximity-Tracing-System für das Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25), dem Bundesgesetz über die gesetzlichen Grundlagen für Verordnungen des Bundesrates zur Bewältigung der Covid-19-Epidemie vom 25. September 2020 (Covid-19-Gesetz; SR 818.102) sowie der Verordnung vom 30. Juni 2021 über ein System zur Benachrichtigung über eine mögliche Ansteckung mit dem Coronavirus Sars-CoV-2 an Veranstaltungen (VBV; SR 818.102.4).
1.3
@@ -65,11 +65,13 @@
Die App ruft periodisch eine Liste der privaten Schlüssel der infizierten Benutzerinnen und Benutzer der SwissCovid-App und anderen interoperablen Apps ab und lässt vom Betriebssystem überprüfen, ob mindestens ein lokal gespeicherter Identifizierungscode mit einem privaten Schlüssel der Liste generiert wurde. Ist dies der Fall und bestand zu mindestens einem Mobiltelefon einer infizierten Benutzerin oder einem infizierten Benutzer der SwissCovid-App oder anderen interoperablen Apps eine Annährung von 1,5 Metern oder weniger und erreicht die Summe der Dauer aller solchen Annäherungen innerhalb eines Tages 15 Minuten, so gibt die App die Benachrichtigung aus. Der Abstand wird anhand der Stärke der empfangenen Signale geschätzt.
- Warnsystem für Veranstaltungen: Die Organisatorin oder der Organisator einer Veranstaltung generiert in seiner App einen QR-Code. Besucherinnen und Besucher der Veranstaltung können mit ihrer App den gezeigten QR-Code scannen und sind so an der Veranstaltung eingecheckt.
+ Warnsystem für Veranstaltungen: Die Organisatorin oder der Organisator einer Veranstaltung generiert in seiner App oder über die Internetseite
+ qr.swisscovid.ch
+ einen QR-Code. Besucherinnen und Besucher der Veranstaltung können mit ihrer App den gezeigten QR-Code scannen und sind so an der Veranstaltung eingecheckt.
3.4
- Die App speichert auf dem Mobiltelefon die Veranstaltungs-Identifizierungscodes mit dem jeweiligen Datum, die Dauer des Aufenthaltes und die Bezeichnung der Veranstaltung. Sie ruft periodisch vom Veranstaltungs-Backend im Abrufverfahren die Liste der Veranstaltungs-Identifizierungscodes der infizierten teilnehmenden Personen ab. Sie gleicht die Veranstaltungs-Identifizierungscodes mit den von ihr lokal gespeicherten Veranstaltungs-Identifizierungscodes ab. Ergibt der Abgleich eine Übereinstimmung, so gibt die App die Benachrichtigung aus.Ist eine Infektion bei einer Benutzerin oder einem Benutzer nachgewiesen, generieren zugriffsberechtigte Fachpersonen (z.B. behandelnde Ärztinnen und Ärzte oder Apothekerinnen und Apotheker) einen einmaligen und zeitlich begrenzt gültigen Freischaltcode («Covidcode») und geben diesen der infizierten Benutzerin oder dem infizierten Benutzer bekannt. Diese oder dieser kann den Freischaltcode in ihre oder seine App freiwillig eingeben. Die Benachrichtigung bzw. die Eingabe des Freischaltcodes erfolgt nur mit der ausdrücklichen Einwilligung der infizierten Benutzerin oder des infizierten Benutzers. Die infizierte Benutzerin oder der infizierte Benutzer kann ausserdem wählen, ob sie oder er sowohl die engen Kontakte aufgrund des Proximity-Tracing-Systems als auch die Kontakte aufgrund der Teilnahme an einer Veranstaltung warnen will. Darüber hinaus kann sie oder er auch entscheiden, ob er für jede Veranstaltung, an welcher er im relevanten Zeitraum teilgenommen hat, eine Benachrichtigung auslösen will oder nur für einzelne.
+ Die App speichert auf dem Mobiltelefon die Veranstaltungs-Identifizierungscodes mit dem jeweiligen Datum, die Dauer des Aufenthaltes und die Bezeichnung der Veranstaltung. Sie ruft periodisch vom Veranstaltungs-Backend im Abrufverfahren die Liste der Veranstaltungs-Identifizierungscodes der infizierten teilnehmenden Personen ab. Sie gleicht die Veranstaltungs-Identifizierungscodes mit den von ihr lokal gespeicherten Veranstaltungs-Identifizierungscodes ab. Ergibt der Abgleich eine Übereinstimmung, so gibt die App die Benachrichtigung aus. Ist eine Infektion bei einer Benutzerin oder einem Benutzer nachgewiesen, generieren zugriffsberechtigte Fachpersonen (z.B. behandelnde Ärztinnen und Ärzte oder Apothekerinnen und Apotheker) einen einmaligen und zeitlich begrenzt gültigen Freischaltcode («Covidcode») und geben diesen der infizierten Benutzerin oder dem infizierten Benutzer bekannt. Diese oder dieser kann den Freischaltcode in ihre oder seine App freiwillig eingeben. Die Benachrichtigung bzw. die Eingabe des Freischaltcodes erfolgt nur mit der ausdrücklichen Einwilligung der infizierten Benutzerin oder des infizierten Benutzers. Die infizierte Benutzerin oder der infizierte Benutzer kann ausserdem wählen, ob sie oder er sowohl die engen Kontakte aufgrund des Proximity-Tracing-Systems als auch die Kontakte aufgrund der Teilnahme an einer Veranstaltung warnen will. Darüber hinaus kann sie oder er auch entscheiden, ob er für jede Veranstaltung, an welcher er im relevanten Zeitraum teilgenommen hat, eine Benachrichtigung auslösen will oder nur für einzelne.
Die anderen Benutzerinnen und Benutzer der SwissCovid-App
diff --git a/app/src/main/assets/disclaimer/en/terms_of_use.html b/app/src/main/assets/disclaimer/en/terms_of_use.html
index 6acec624d..1410ce32e 100644
--- a/app/src/main/assets/disclaimer/en/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/en/terms_of_use.html
@@ -65,14 +65,13 @@
The app periodically retrieves a list of the private keys of users of the SwissCovid app and other interoperable apps known to be infected and allows the operating system to check whether at least one locally stored identification code was generated by a private key included in the list. If this is the case, and if proximity of 1.5 metres or less to the mobile phone of at least one infected user of the SwissCovid app or another interoperable app was registered, and if the duration of all such proximity events within one day amounts to at least 15 minutes, then the app issues a notification. Proximity is estimated on the basis of the strength of the signals received.
- Warning system for events: The organiser of an event generates a QR code in their app. People attending the event can use their app to scan the displayed QR code and are thus checked in to the event.
-
-
- The app saves on the mobile phone the event identification codes with the relevant date, duration of attendance and designation of the event. It periodically retrieves from the event back end the list of event identification codes of the infected participants. It compares these event participation codes with the event participation codes stored locally by the app. If there is a match, the app issues the notification.
+ Warning system for events: The organiser of an event generates a QR code in their app or on the website
+ qr.swisscovid.ch
+ . People attending the event can use their app to scan the displayed QR code and are thus checked in to the event.
3.4
- If an infection is confirmed in a user, experts with access rights (e.g. attending physicians or pharmacists) generate a unique activation code (Covid code), valid for a limited period, which they disclose to the infected user. This user can voluntarily enter the activation code in the app. Notification, or entry of the activation code, occurs only with the explicit consent of the infected user. The infected user can also choose whether they want to warn close contacts on the basis of the proximity tracing system and contacts on the basis of participation in an event. In addition, they can decide whether they want to trigger a notification for every event they attended during the relevant period or only for individual events.
+ The app saves on the mobile phone the event identification codes with the relevant date, duration of attendance and designation of the event. It periodically retrieves from the event back end the list of event identification codes of the infected participants. It compares these event participation codes with the event participation codes stored locally by the app. If there is a match, the app issues the notification. If an infection is confirmed in a user, experts with access rights (e.g. attending physicians or pharmacists) generate a unique activation code (Covid code), valid for a limited period, which they disclose to the infected user. This user can voluntarily enter the activation code in the app. Notification, or entry of the activation code, occurs only with the explicit consent of the infected user. The infected user can also choose whether they want to warn close contacts on the basis of the proximity tracing system and contacts on the basis of participation in an event. In addition, they can decide whether they want to trigger a notification for every event they attended during the relevant period or only for individual events.
Other users of the SwissCovid app or other interoperable apps who came into proximity, as defined in Section 3.3, with the infected user during the infectious period or participated in the same event at the same time are notified by their own apps.
diff --git a/app/src/main/assets/disclaimer/fr/terms_of_use.html b/app/src/main/assets/disclaimer/fr/terms_of_use.html
index 4e671b083..ffe7e4348 100644
--- a/app/src/main/assets/disclaimer/fr/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/fr/terms_of_use.html
@@ -28,7 +28,7 @@
818.102
-) et l’ordonnance du 30 juin 2021 sur un système visant à informer d’une infection possible au coronavirus SARS-CoV-2 lors de manifestations (OSIM ; RS 818.102.4).
+) et l’ordonnance du 30 juin 2021 sur un système visant à informer d’une infection possible au coronavirus SARS-CoV-2 lors de manifestations (OSIM; RS 818.102.4).
1.3
L’application vise à informer les utilisateurs qui pourraient avoir été exposés au virus et à établir des statistiques en rapport avec le coronavirus.
@@ -75,17 +75,16 @@
À intervalles réguliers, l’application extrait la liste des clés privées des utilisateurs infectés de l’application SwissCovid ou d’autres applications interopérables, et le système d’exploitation contrôle si au moins un code d’identification enregistré localement a été généré avec une clé privée. Si tel est le cas et qu’un rapprochement d’un mètre et demi ou moins a été établi avec au moins un téléphone mobile d’au moins un utilisateur infecté de l’application SwissCovid ou d’autres applications interopérables et que la durée totale de ces rapprochements atteint ou dépasse les quinze minutes au cours de la même journée, l’application envoie une information. La distance est évaluée en fonction de l’intensité du signal reçu.
- Système d’alerte pour les manifestations : l’organisateur d’une manifestation génère un code QR dans son application. Les visiteurs de la manifestation peuvent scanner le code QR présenté avec leur application et indiquent ainsi avoir participé à la manifestation.
-
-
- L’application enregistre sur le téléphone portable le code d’identification de la manifestation avec sa date, sa durée et sa description. Depuis le
-
- backend
-
- manifestations, elle consulte régulièrement les codes d’identification de la manifestation chez les participants infectés. Elle compare ces codes avec ceux qu’elle a stocké localement. Si deux codes sont les mêmes, elle envoie alors une notification.
+ Système d’alerte pour les manifestations : l’organisateur d’une manifestation génère un code QR dans son application ou sur le site Internet
+ qr.swisscovid.ch
+ Les visiteurs de la manifestation peuvent scanner le code QR présenté avec leur application et indiquent ainsi avoir participé à la manifestation.
+L’application enregistre sur le téléphone portable le code d’identification de la manifestation avec sa date, sa durée et sa description. Depuis le
+
+ backend
+
+manifestations, elle consulte régulièrement les codes d’identification de la manifestation chez les participants infectés. Elle compare ces codes avec ceux qu’elle a stocké localement. Si deux codes sont les mêmes, elle envoie alors une notification
- 3.4
Si une infection est attestée chez un utilisateur, les professionnels disposant des droits d’accès (p. ex. les médecins traitants ou les pharmaciens) génèrent un code d’autorisation unique et temporaire (code COVID) et le communiquent à l’utilisateur infecté. Ce dernier peut saisir de manière volontaire le code d’autorisation dans son application. La notification et la saisie du code d’autorisation ne se font qu’avec le consentement explicite de la personne infectée. Cette dernière peut par ailleurs choisir si elle souhaite alerter non seulement les contacts étroits détectés par le système de traçage de proximité mais également ceux détectés en raison de leur présence à une manifestation. Elle peut aussi décider de déclencher une notification pour toutes les manifestations auxquelles elle a participé pendant la période concernée ou seulement pour certaines d’entre elles.
diff --git a/app/src/main/assets/disclaimer/it/terms_of_use.html b/app/src/main/assets/disclaimer/it/terms_of_use.html
index 43a0f0f61..da43e2fe3 100644
--- a/app/src/main/assets/disclaimer/it/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/it/terms_of_use.html
@@ -65,14 +65,13 @@
L’app richiama periodicamente un elenco delle chiavi private degli utenti infetti dell’app SwissCovid e delle altre app interoperabili e controlla con il suo sistema operativo se almeno un codice d’identificazione memorizzato localmente è stato generato con una chiave privata dell’elenco. Se ciò si verifica nonché la prossimità da almeno un telefono cellulare di un utente dell’app SwissCovid o delle altre app interoperabili infetto è pari o inferiore a 1,5 metri e la somma della durata di tutte queste prossimità in un giorno raggiunge i quindici minuti, l’app ne informa l’utente. La distanza è stimata in base alla potenza del segnale ricevuto.
- Sistema di allerta per le manifestazioni: l’organizzatore di una manifestazione genera nella propria app un codice QR che i visitatori della manifestazione possono scansionare per effettuare il check-in alla manifestazione.
-
-
- L’app memorizza sul telefono cellulare il codice di identificazione della manifestazione con la rispettiva data, la durata della permanenza e la designazione della manifestazione. Richiama periodicamente dal back end delle manifestazioni l’elenco dei codici di identificazione delle manifestazioni dei partecipanti infetti. Confronta tali codici con i codici di identificazione delle manifestazioni che ha memorizzato localmente. Se da tale confronto emerge una corrispondenza, genera una notifica.
+ Sistema di allerta per le manifestazioni: l’organizzatore di una manifestazione genera nella propria app o tramite la pagina Internet
+ qr.swisscovid.ch
+ un codice QR che i visitatori della manifestazione possono scansionare per effettuare il check-in alla manifestazione.
3.4
- In caso di infezione accertata di un utente, il personale specialistico avente diritto di accesso (p. es. medici curanti o farmacisti) genera un codice di attivazione (codice Covid) univoco e la cui validità è limitata nel tempo e lo comunica all’utente infetto, che può immetterlo nell’app su base volontaria. La notifica ovvero l’inserimento del codice di attivazione richiedono il consenso esplicito dell’utente infetto. L’utente infetto può scegliere se allertare sia i contatti stretti in base al sistema di tracciamento della prossimità sia i contatti in base alla partecipazione a una manifestazione. Può inoltre decidere se generare una notifica per tutte le manifestazioni alle quali ha partecipato nel periodo rilevante o solo per alcune.
+ L’app memorizza sul telefono cellulare il codice di identificazione della manifestazione con la rispettiva data, la durata della permanenza e la designazione della manifestazione. Richiama periodicamente dal back end delle manifestazioni l’elenco dei codici di identificazione delle manifestazioni dei partecipanti infetti. Confronta tali codici con i codici di identificazione delle manifestazioni che ha memorizzato localmente. Se da tale confronto emerge una corrispondenza, genera una notifica. In caso di infezione accertata di un utente, il personale specialistico avente diritto di accesso (p. es. medici curanti o farmacisti) genera un codice di attivazione (codice Covid) univoco e la cui validità è limitata nel tempo e lo comunica all’utente infetto, che può immetterlo nell’app su base volontaria. La notifica ovvero l’inserimento del codice di attivazione richiedono il consenso esplicito dell’utente infetto. L’utente infetto può scegliere se allertare sia i contatti stretti in base al sistema di tracciamento della prossimità sia i contatti in base alla partecipazione a una manifestazione. Può inoltre decidere se generare una notifica per tutte le manifestazioni alle quali ha partecipato nel periodo rilevante o solo per alcune.
Gli altri utenti dell’app SwissCovid o delle altre app interoperabili che sono stati in prossimità secondo il numero 3.3 con l’utente infetto durante il periodo di infettività o che hanno partecipato nello stesso momento alla medesima manifestazione vengono informati dalla loro app.
diff --git a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt
index d9551cd2e..1fa1ef840 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt
@@ -20,8 +20,9 @@ import androidx.fragment.app.FragmentActivity
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import ch.admin.bag.dp3t.checkin.CheckinOverviewFragment
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.checkinflow.AlreadyCheckedInErrorDialog
import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment
-import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment
+import ch.admin.bag.dp3t.checkin.checkout.CheckOutFragment
import ch.admin.bag.dp3t.checkin.models.CheckInState
import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState
import ch.admin.bag.dp3t.checkin.networking.CrowdNotifierKeyLoadWorker
@@ -47,10 +48,8 @@ import org.crowdnotifier.android.sdk.utils.QrUtils.*
import org.dpppt.android.sdk.DP3T
import java.nio.charset.StandardCharsets
-
class MainActivity : FragmentActivity() {
-
companion object {
const val ACTION_EXPOSED_GOTO_REPORTS = "ACTION_EXPOSED_GOTO_REPORTS"
const val ACTION_INFORMED_GOTO_REPORTS = "ACTION_INFORMED_GOTO_REPORTS"
@@ -129,7 +128,6 @@ class MainActivity : FragmentActivity() {
}
private fun onOnboardingFinished(onboardingType: OnboardingType, activityResult: ActivityResult, qrCodeUrl: String? = null) {
-
if (activityResult.resultCode == RESULT_OK) {
secureStorage.lastShownUpdateBoardingVersion = UPDATE_BOARDING_VERSION
secureStorage.onboardingCompleted = true
@@ -224,7 +222,9 @@ class MainActivity : FragmentActivity() {
if (crowdNotifierViewModel.checkInState.venueInfo == venueInfo) {
showCheckOutFragment()
} else {
- ErrorDialog(this, CrowdNotifierErrorState.ALREADY_CHECKED_IN).show()
+ AlreadyCheckedInErrorDialog(this)
+ .setOnCheckoutListener { showCheckOutFragment() }
+ .show()
}
} else {
crowdNotifierViewModel.checkInState = CheckInState(
@@ -242,7 +242,9 @@ class MainActivity : FragmentActivity() {
try {
val venueInfo = CrowdNotifier.getVenueInfo(qrCodeUrl, BuildConfig.ENTRY_QR_CODE_HOST)
if (crowdNotifierViewModel.isCheckedIn.value == true) {
- ErrorDialog(this, CrowdNotifierErrorState.ALREADY_CHECKED_IN).show()
+ AlreadyCheckedInErrorDialog(this)
+ .setOnCheckoutListener { showCheckOutFragment() }
+ .show()
} else {
crowdNotifierViewModel.performCheckinAndSetReminders(venueInfo, System.currentTimeMillis(), 0)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.kt
index d69868b98..5db53f079 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.kt
@@ -11,7 +11,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
-import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment
+import ch.admin.bag.dp3t.checkin.checkout.CheckOutFragment
import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment
import ch.admin.bag.dp3t.checkin.diary.DiaryFragment
import ch.admin.bag.dp3t.checkin.generateqrcode.EventsOverviewFragment
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
index 6c0456e0b..0adf02b7a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
@@ -44,6 +44,8 @@ public class CrowdNotifierViewModel extends AndroidViewModel {
private final MutableLiveData isCheckedIn = new MutableLiveData<>(false);
private CheckInState checkInState;
+ private boolean isResolvingCheckoutConflicts = false;
+
private SecureStorage storage;
private final Handler handler = new Handler(Looper.getMainLooper());
private Runnable timeUpdateRunnable;
@@ -138,7 +140,6 @@ public void refreshTraceKeys() {
}
private void refreshTraceKeyLoadingError() {
-
if (storage.getLastSuccessfulCheckinDownload() <= System.currentTimeMillis() - MAX_DURATION_WITHOUT_SUCCESSFUL_DOWNLOAD) {
hasTraceKeyDownloadError.setValue(true);
} else {
@@ -215,6 +216,14 @@ public void performCheckinAndSetReminders(VenueInfo venueInfo, long checkinTime,
CrowdNotifierReminderHelper.setReminder(currentTime + selectedReminderDelay, getApplication());
}
+ public boolean isResolvingCheckoutConflicts() {
+ return isResolvingCheckoutConflicts;
+ }
+
+ public void setResolvingCheckoutConflicts(boolean resolvingCheckoutConflicts) {
+ isResolvingCheckoutConflicts = resolvingCheckoutConflicts;
+ }
+
@Override
public void onCleared() {
super.onCleared();
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/EditCheckinBaseFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/EditCheckinBaseFragment.kt
index 183083e98..5eed5a9cb 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/EditCheckinBaseFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/EditCheckinBaseFragment.kt
@@ -8,7 +8,9 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.checkin.checkout.checkForOverlap
import ch.admin.bag.dp3t.checkin.models.CheckinInfo
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.databinding.FragmentCheckOutAndEditBinding
import ch.admin.bag.dp3t.extensions.getSwissCovidLocationData
@@ -44,12 +46,6 @@ abstract class EditCheckinBaseFragment : Fragment() {
return
}
- val hasOverlapWithOtherCheckin = checkForOverlap(checkinInfo, context)
- if (hasOverlapWithOtherCheckin) {
- showSavingNotPossibleDialog(getString(R.string.checkout_overlapping_alert_description), context)
- return
- }
-
val checkinDuration = checkinInfo.checkOutTime - checkinInfo.checkInTime
val maxCheckinTime = checkinInfo.venueInfo.getSwissCovidLocationData().automaticCheckoutDelaylMs
if (checkinDuration > maxCheckinTime) {
@@ -59,16 +55,21 @@ abstract class EditCheckinBaseFragment : Fragment() {
return
}
+ val overlappingCheckins = DiaryStorage.getInstance(context).checkForOverlap(checkinInfo)
+ if (overlappingCheckins.isNotEmpty()) {
+ handleOverlap(overlappingCheckins)
+ return
+ }
+
saveEntry()
}
- abstract fun saveEntry()
-
- private fun checkForOverlap(diaryEntry: CheckinInfo, context: Context): Boolean {
- val otherCheckins = DiaryStorage.getInstance(context).entries.filter { it.id != diaryEntry.id }
- return otherCheckins.any { it.checkOutTime > diaryEntry.checkInTime && diaryEntry.checkOutTime > it.checkInTime }
+ open fun handleOverlap(overlappingCheckins: Collection) {
+ showSavingNotPossibleDialog(getString(R.string.checkout_overlapping_alert_description), requireContext())
}
+ abstract fun saveEntry()
+
private fun showSavingNotPossibleDialog(message: String, context: Context) {
AlertDialog.Builder(context, R.style.NextStep_AlertDialogStyle)
.setTitle(R.string.checkout_overlapping_alert_title)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/AlreadyCheckedInErrorDialog.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/AlreadyCheckedInErrorDialog.kt
new file mode 100644
index 000000000..1b5e3d426
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/AlreadyCheckedInErrorDialog.kt
@@ -0,0 +1,40 @@
+package ch.admin.bag.dp3t.checkin.checkinflow
+
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
+import ch.admin.bag.dp3t.R
+
+class AlreadyCheckedInErrorDialog(context: Context) : AlertDialog(context) {
+
+ private var onCheckoutListener: (() -> Unit)? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.dialog_already_checkedin)
+
+ val closeButton = findViewById(R.id.already_checkedin_close_button)
+ closeButton?.setOnClickListener { dismiss() }
+
+ val checkoutButton = findViewById(R.id.already_checkedin_checkout_button)
+ checkoutButton?.setOnClickListener {
+ dismiss()
+ onCheckoutListener?.invoke()
+ }
+
+ val cancelButton = findViewById(R.id.already_checkedin_cancel_button)
+ cancelButton?.setOnClickListener { dismiss() }
+
+ window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ window?.setBackgroundDrawableResource(R.drawable.dialog_background)
+ }
+
+ fun setOnCheckoutListener(onCheckoutListener: () -> Unit): AlreadyCheckedInErrorDialog {
+ this.onCheckoutListener = onCheckoutListener
+ return this
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.kt
index 63c55060f..320995dc7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.kt
@@ -98,8 +98,10 @@ class QrCodeScannerFragment : Fragment(), QrCodeAnalyzer.Listener {
camera.cameraInfo.torchState.observe(viewLifecycleOwner, { v: Int ->
if (v == TorchState.ON) {
flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_light_on))
+ flashButton.contentDescription = getString(R.string.accessibility_camera_light_on)
} else {
flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_light_off))
+ flashButton.contentDescription = getString(R.string.accessibility_camera_light_off)
}
})
flashButton.setOnClickListener { camera.cameraControl.enableTorch(camera.cameraInfo.torchState.value == TorchState.OFF) }
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictDialogFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictDialogFragment.kt
new file mode 100644
index 000000000..0c256d1f2
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictDialogFragment.kt
@@ -0,0 +1,99 @@
+package ch.admin.bag.dp3t.checkin.checkout
+
+import android.content.DialogInterface
+import android.os.Bundle
+import android.text.SpannableString
+import android.text.SpannableStringBuilder
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.text.bold
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.activityViewModels
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.checkout.CheckoutConflictRecyclerViewAdapter.ConflictingVenueVisitItem
+import ch.admin.bag.dp3t.checkin.diary.EditDiaryEntryFragment
+import ch.admin.bag.dp3t.checkin.models.CheckInState
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
+import ch.admin.bag.dp3t.databinding.DialogFragmentCheckoutConflictBinding
+import ch.admin.bag.dp3t.extensions.replace
+import ch.admin.bag.dp3t.extensions.showFragment
+import org.crowdnotifier.android.sdk.model.VenueInfo
+import java.util.regex.Pattern
+
+class CheckOutConflictDialogFragment : DialogFragment() {
+
+ companion object {
+ val TAG = CheckOutConflictDialogFragment::class.java.canonicalName
+
+ private const val ARG_CHECKIN_TIME = "CHECKIN_TIME"
+ private const val ARG_CHECKOUT_TIME = "CHECKOUT_TIME"
+
+ fun newInstance(checkinTime: Long, checkoutTime: Long): CheckOutConflictDialogFragment {
+ val fragment = CheckOutConflictDialogFragment()
+ fragment.arguments = Bundle().apply {
+ putLong(ARG_CHECKIN_TIME, checkinTime)
+ putLong(ARG_CHECKOUT_TIME, checkoutTime)
+ }
+ return fragment
+ }
+ }
+
+ private val viewModel: CrowdNotifierViewModel by activityViewModels()
+
+ private lateinit var venueInfo: VenueInfo
+ private lateinit var checkInState: CheckInState
+
+ private lateinit var diaryStorage: DiaryStorage
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ diaryStorage = DiaryStorage.getInstance(context)
+
+ checkInState = viewModel.checkInState?.copy(
+ checkInTime = requireArguments().getLong(ARG_CHECKIN_TIME),
+ checkOutTime = requireArguments().getLong(ARG_CHECKOUT_TIME)
+ ) ?: return
+ venueInfo = checkInState.venueInfo
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return DialogFragmentCheckoutConflictBinding.inflate(inflater, container, false).apply {
+ checkoutConflictText.text =
+ SpannableString(getText(R.string.checkin_overlap_popup_text)).replace(Pattern.compile("\\{CHECKIN\\}")) { _, _ ->
+ SpannableStringBuilder().bold { append(venueInfo.title) }
+ }
+
+ val conflictingItems = diaryStorage.checkForOverlap(checkInState)
+ .sortedBy { it.checkInTime }
+ .map { diaryEntry ->
+ ConflictingVenueVisitItem(diaryEntry) {
+ viewModel.isResolvingCheckoutConflicts = true
+ dismiss()
+ showFragment(EditDiaryEntryFragment.newInstance(diaryEntry.id), modalAnimation = true)
+ }
+ }
+
+ checkoutConflictList.adapter = CheckoutConflictRecyclerViewAdapter().apply {
+ setData(conflictingItems)
+ }
+
+ checkoutConflictBackButton.setOnClickListener {
+ viewModel.isResolvingCheckoutConflicts = false
+ dismiss()
+ }
+ }.root
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ super.onCancel(dialog)
+ viewModel.isResolvingCheckoutConflicts = false
+ }
+
+ override fun onResume() {
+ requireDialog().window!!.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ super.onResume()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictResolvedDialogFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictResolvedDialogFragment.kt
new file mode 100644
index 000000000..057fdb880
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutConflictResolvedDialogFragment.kt
@@ -0,0 +1,82 @@
+package ch.admin.bag.dp3t.checkin.checkout
+
+import android.os.Bundle
+import android.text.SpannableString
+import android.text.SpannableStringBuilder
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.core.text.bold
+import androidx.fragment.app.DialogFragment
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.extensions.replace
+import ch.admin.bag.dp3t.util.StringUtil.getHourMinuteTimeString
+import java.util.regex.Pattern
+
+class CheckOutConflictResolvedDialogFragment : DialogFragment() {
+
+ companion object {
+ val TAG = CheckOutConflictResolvedDialogFragment::class.java.canonicalName
+
+ private const val ARG_LOCATION = "LOCATION"
+ private const val ARG_CHECKIN_TIME = "CHECKIN_TIME"
+ private const val ARG_CHECKOUT_TIME = "CHECKOUT_TIME"
+
+ fun newInstance(location: String, checkinTime: Long, checkoutTime: Long): CheckOutConflictResolvedDialogFragment {
+ val fragment = CheckOutConflictResolvedDialogFragment()
+ fragment.arguments = Bundle().apply {
+ putString(ARG_LOCATION, location)
+ putLong(ARG_CHECKIN_TIME, checkinTime)
+ putLong(ARG_CHECKOUT_TIME, checkoutTime)
+ }
+ return fragment
+ }
+ }
+
+ private var onCheckoutListener: (() -> Unit)? = null
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.dialog_fragment_checkout_conflict_resolved, container)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val closeButton = view.findViewById(R.id.checkout_resolved_close_button)
+ closeButton.setOnClickListener { dismiss() }
+
+ val location = requireArguments().getString(ARG_LOCATION)
+ val checkinTime = requireArguments().getLong(ARG_CHECKIN_TIME)
+ val checkoutTime = requireArguments().getLong(ARG_CHECKOUT_TIME)
+
+ val checkoutResolvedText = view.findViewById(R.id.checkout_resolved_text)
+ checkoutResolvedText.text =
+ SpannableString(getText(R.string.checkin_overlap_popup_success_text)).replace(Pattern.compile("\\{CHECKIN\\}")) { _, _ ->
+ SpannableStringBuilder().bold { append(location) }
+ }
+
+ val checkoutResolvedLocation = view.findViewById(R.id.checkout_resolved_location)
+ checkoutResolvedLocation.text = location
+
+ val checkoutResolvedTime = view.findViewById(R.id.checkout_resolved_time)
+ val start = getHourMinuteTimeString(checkinTime, ":")
+ val end = getHourMinuteTimeString(checkoutTime, ":")
+ checkoutResolvedTime.text = "$start – $end"
+
+ val submitButton = view.findViewById(R.id.checkout_resolved_submit_button)
+ submitButton.setOnClickListener {
+ dismiss()
+ onCheckoutListener?.invoke()
+ }
+ }
+
+ override fun onResume() {
+ requireDialog().window!!.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ super.onResume()
+ }
+
+ fun setOnCheckoutListener(onCheckoutListener: () -> Unit): CheckOutConflictResolvedDialogFragment {
+ this.onCheckoutListener = onCheckoutListener
+ return this
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutFragment.kt
similarity index 70%
rename from app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutFragment.kt
index a2e9828f4..08d7b4a18 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckOutFragment.kt
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.checkin.checkinflow
+package ch.admin.bag.dp3t.checkin.checkout
import android.os.Bundle
import android.view.View
@@ -9,9 +9,9 @@ import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
import ch.admin.bag.dp3t.checkin.EditCheckinBaseFragment
import ch.admin.bag.dp3t.checkin.models.CheckInState
+import ch.admin.bag.dp3t.checkin.models.CheckinInfo
import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
-import ch.admin.bag.dp3t.checkin.models.CheckinInfo
import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper
import ch.admin.bag.dp3t.checkin.utils.NotificationHelper
import ch.admin.bag.dp3t.databinding.FragmentCheckOutAndEditBinding
@@ -35,6 +35,11 @@ class CheckOutFragment : EditCheckinBaseFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
+ if (savedInstanceState == null) {
+ viewModel.isResolvingCheckoutConflicts = false
+ }
+
checkIfAutoCheckoutHappened()
checkInState = viewModel.checkInState?.copy(checkOutTime = System.currentTimeMillis()) ?: return
venueInfo = checkInState.venueInfo
@@ -58,6 +63,30 @@ class CheckOutFragment : EditCheckinBaseFragment() {
checkoutPrimaryButton.setText(R.string.checkout_button_title)
checkoutPrimaryButton.setOnClickListener { performSave() }
}
+
+ if (viewModel.isResolvingCheckoutConflicts) {
+ val overlappingCheckins = DiaryStorage.getInstance(context).checkForOverlap(checkinInfo)
+ if (overlappingCheckins.isNotEmpty()) {
+ showConflictResolutionDialog()
+ } else {
+ showConflictResolutionCompletedDialog()
+ viewModel.isResolvingCheckoutConflicts = false
+ }
+ }
+ }
+
+ override fun handleOverlap(overlappingCheckins: Collection) {
+ showConflictResolutionDialog()
+ }
+
+ private fun showConflictResolutionDialog() {
+ CheckOutConflictDialogFragment.newInstance(checkInState.checkInTime, checkInState.checkOutTime).show(parentFragmentManager, CheckOutConflictDialogFragment.TAG)
+ }
+
+ private fun showConflictResolutionCompletedDialog() {
+ CheckOutConflictResolvedDialogFragment.newInstance(venueInfo.title, checkInState.checkInTime, checkInState.checkOutTime)
+ .setOnCheckoutListener { performSave() }
+ .show(parentFragmentManager, CheckOutConflictResolvedDialogFragment.TAG)
}
override fun saveEntry() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutConflictRecyclerViewAdapter.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutConflictRecyclerViewAdapter.java
new file mode 100644
index 000000000..09762af66
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutConflictRecyclerViewAdapter.java
@@ -0,0 +1,97 @@
+package ch.admin.bag.dp3t.checkin.checkout;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
+import ch.admin.bag.dp3t.util.StringUtil;
+
+public class CheckoutConflictRecyclerViewAdapter
+ extends RecyclerView.Adapter {
+
+ private final List diaryItems = new ArrayList<>();
+
+ private ConflictingVenueVisitItem getItem(int position) {
+ return diaryItems.get(position);
+ }
+
+ @NonNull
+ @Override
+ public ConflictingVenueVisitViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ConflictingVenueVisitViewHolder(
+ LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkout_conflicting_venue_visit, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ConflictingVenueVisitViewHolder holder, int position) {
+ holder.bind(getItem(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return diaryItems.size();
+ }
+
+ public void setData(List items) {
+ diaryItems.clear();
+ diaryItems.addAll(items);
+ notifyDataSetChanged();
+ }
+
+
+ public static class ConflictingVenueVisitViewHolder extends RecyclerView.ViewHolder {
+
+ private final TextView timeTextView;
+ private final TextView nameTextView;
+ private final View nameEditButton;
+
+ public ConflictingVenueVisitViewHolder(View itemView) {
+ super(itemView);
+ this.timeTextView = itemView.findViewById(R.id.item_conflicting_entry_time);
+ this.nameTextView = itemView.findViewById(R.id.item_conflicting_entry_name);
+ this.nameEditButton = itemView.findViewById(R.id.item_conflicting_entry_edit);
+ }
+
+ public void bind(ConflictingVenueVisitItem item) {
+ VenueInfo venueInfo = item.getDiaryEntry().getVenueInfo();
+ nameTextView.setText(venueInfo.getTitle());
+ String start = StringUtil.getHourMinuteTimeString(item.getDiaryEntry().getCheckInTime(), ":");
+ String end = StringUtil.getHourMinuteTimeString(item.getDiaryEntry().getCheckOutTime(), ":");
+ timeTextView.setText(start + " – " + end);
+ nameEditButton.setOnClickListener(item.getOnClickListener());
+ }
+
+ }
+
+
+ public static class ConflictingVenueVisitItem {
+
+ private final DiaryEntry diaryEntry;
+ private final View.OnClickListener onClickListener;
+
+ public ConflictingVenueVisitItem(DiaryEntry diaryEntry, View.OnClickListener onClickListener) {
+ this.diaryEntry = diaryEntry;
+ this.onClickListener = onClickListener;
+ }
+
+ public DiaryEntry getDiaryEntry() {
+ return diaryEntry;
+ }
+
+ public View.OnClickListener getOnClickListener() {
+ return onClickListener;
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutUtils.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutUtils.kt
new file mode 100644
index 000000000..534fb0429
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkout/CheckoutUtils.kt
@@ -0,0 +1,10 @@
+package ch.admin.bag.dp3t.checkin.checkout
+
+import ch.admin.bag.dp3t.checkin.models.CheckinInfo
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
+
+fun DiaryStorage.checkForOverlap(diaryEntry: CheckinInfo): Collection {
+ val otherCheckins = this.entries.filter { it.id != diaryEntry.id }
+ return otherCheckins.filter { it.checkOutTime > diaryEntry.checkInTime && diaryEntry.checkOutTime > it.checkInTime }
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt
index 1bb6fc321..f1367240b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt
@@ -9,7 +9,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
-import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment
+import ch.admin.bag.dp3t.checkin.checkout.CheckOutFragment
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisit
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitCurrent
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitDayHeader
@@ -68,6 +68,7 @@ class DiaryFragment : Fragment() {
}
val isEmpty = diaryEntries.isEmpty()
checkinDiaryEmptyView.isVisible = isEmpty
+ checkinDiaryRecyclerView.isVisible = !isEmpty
var daysAgoString = ""
for (diaryEntry in diaryEntries) {
val newDaysAgoString: String = DateUtils.getFormattedWeekdayWithDate(diaryEntry.checkInTime, requireContext())
diff --git a/app/src/main/java/ch/admin/bag/dp3t/contacts/ContactsFragment.java b/app/src/main/java/ch/admin/bag/dp3t/contacts/ContactsFragment.java
index 1d7697188..fed7979ff 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/contacts/ContactsFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/contacts/ContactsFragment.java
@@ -144,7 +144,10 @@ private void setupHistoryCard(View view) {
break;
}
}
- if (timeSync == null) lastSyncDate.setText("-");
+ if (timeSync == null) {
+ lastSyncDate.setText("\u2013");
+ lastSyncDate.setContentDescription(getString(R.string.synchronizations_view_empty_list));
+ }
historyCardLoadingView.animate()
.alpha(0f)
.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime))
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/SpannableExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/SpannableExtensions.kt
new file mode 100644
index 000000000..79572825d
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/SpannableExtensions.kt
@@ -0,0 +1,30 @@
+package ch.admin.bag.dp3t.extensions
+
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import java.util.regex.MatchResult
+import java.util.regex.Pattern
+
+/**
+ * Search and replace in a spannable text using regular expressions, while keeping spans and allowing to add and remove spans.
+ * @param pattern regular expression to match.
+ * @param callback to replace the matched sequence with a new spanned value.
+ */
+fun Spannable.replace(
+ pattern: Pattern,
+ callback: (MatchResult, Spanned) -> Spanned
+): Spannable {
+ val matcher = pattern.matcher(toString())
+ .useAnchoringBounds(false)
+ .useTransparentBounds(true)
+ val result = SpannableStringBuilder(this)
+ while (matcher.find()) {
+ val matchResult = matcher.toMatchResult()
+ val matchedSequence = result.subSequence(matchResult.start(), matchResult.end()) as Spanned
+ val replacement = callback(matchResult, matchedSequence)
+ result.replace(matchResult.start(), matchResult.end(), replacement)
+ matcher.reset(result).region(matchResult.start() + replacement.length, result.length)
+ }
+ return result
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/HomeFragment.java b/app/src/main/java/ch/admin/bag/dp3t/home/HomeFragment.java
index d718e2029..735762b0b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/HomeFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/HomeFragment.java
@@ -45,7 +45,7 @@
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.checkin.CheckinOverviewFragment;
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
-import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment;
+import ch.admin.bag.dp3t.checkin.checkout.CheckOutFragment;
import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment;
import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
import ch.admin.bag.dp3t.contacts.ContactsFragment;
@@ -133,6 +133,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
setupScrollBehavior();
setupCovidCodeCard();
+ AccessibilityUtil.setButtonAccessibilityDelegate(tracingCard);
+ AccessibilityUtil.setButtonAccessibilityDelegate(cardNotifications);
+ AccessibilityUtil.setButtonAccessibilityDelegate(checkinCard);
+ AccessibilityUtil.setButtonAccessibilityDelegate(covidCodeCard);
+
showEndIsolationDialogIfNecessary();
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt
index cca0b3039..c1fb10af6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt
@@ -24,6 +24,8 @@ import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.PhoneUtil
+import ch.admin.bag.dp3t.util.isAccessibilityActive
+import ch.admin.bag.dp3t.util.requestAccessibilityFocus
import org.dpppt.android.sdk.DP3T
private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}"
@@ -46,7 +48,12 @@ class InformFragment : TraceKeyShareBaseFragment() {
override fun onResume() {
super.onResume()
- binding.covidcodeInput.requestFocus()
+
+ if (requireContext().isAccessibilityActive()) {
+ binding.informTitle.requestAccessibilityFocus()
+ } else {
+ binding.covidcodeInput.requestFocus()
+ }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformIntroFragment.java b/app/src/main/java/ch/admin/bag/dp3t/inform/InformIntroFragment.java
index 560a4dc46..19b422640 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformIntroFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformIntroFragment.java
@@ -23,6 +23,7 @@
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.storage.SecureStorage;
import ch.admin.bag.dp3t.travel.TravelUtils;
+import ch.admin.bag.dp3t.util.AccessibilityUtil;
public class InformIntroFragment extends Fragment {
@@ -42,6 +43,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
});
((InformActivity) requireActivity()).allowBackButton(true);
+ AccessibilityUtil.requestAccessibilityFocus(view.findViewById(R.id.inform_intro_title));
+
SecureStorage secureStorage = SecureStorage.getInstance(getContext());
List countries = secureStorage.getInteropCountries();
ViewGroup countriesContainer = view.findViewById(R.id.inform_intro_travel);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/views/ChainedEditText.java b/app/src/main/java/ch/admin/bag/dp3t/inform/views/ChainedEditText.java
index be2003493..e2f3d1efe 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/views/ChainedEditText.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/views/ChainedEditText.java
@@ -72,6 +72,7 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
shadowEditText = new EditText(context);
shadowEditText.setHeight(1);
shadowEditText.setWidth(1);
+ shadowEditText.setHint(R.string.inform_code_title);
shadowEditText.setBackgroundColor(Color.TRANSPARENT);
shadowEditText.setCursorVisible(false);
shadowEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
@@ -119,6 +120,8 @@ public void afterTextChanged(Editable s) {
focusEditText();
}
});
+
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
private void focusEditText() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt
index 17d29baf6..59b441839 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt
@@ -12,7 +12,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
-import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment
+import ch.admin.bag.dp3t.checkin.checkout.CheckOutFragment
import ch.admin.bag.dp3t.databinding.FragmentInfoTabBinding
import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.inform.InformActivity
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingDisclaimerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingDisclaimerFragment.java
index 7fa55855d..69b04653f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingDisclaimerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingDisclaimerFragment.java
@@ -21,6 +21,7 @@
import androidx.fragment.app.Fragment;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.util.AccessibilityUtil;
import ch.admin.bag.dp3t.util.AssetUtil;
import ch.admin.bag.dp3t.util.UlTagHandler;
import ch.admin.bag.dp3t.util.UrlUtil;
@@ -51,10 +52,13 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
view.findViewById(R.id.onboarding_disclaimer_data_protection_to_online_version_button);
View termsOfUseToOnlineVersionButton = view.findViewById(R.id.onboarding_disclaimer_terms_of_use_to_online_version_button);
+ View termsOfUseHeader = view.findViewById(R.id.conditions_of_use_header_container);
View termsOfUseContainer = view.findViewById(R.id.onboarding_disclaimer_terms_of_use_container);
+ View dataProtectionHeader = view.findViewById(R.id.data_protection_header_container);
View dataProtectionContainer = view.findViewById(R.id.onboarding_disclaimer_data_protection_container);
- view.findViewById(R.id.data_protection_header_container).setOnClickListener(v -> {
+ AccessibilityUtil.setExpansionToggleStateAccessibilityDelegate(dataProtectionHeader, dataProtectionContainer);
+ dataProtectionHeader.setOnClickListener(v -> {
if (dataProtectionContainer.getVisibility() == View.VISIBLE) {
dataProtectionContainer.setVisibility(View.GONE);
v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.white));
@@ -68,7 +72,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
.start();
});
- view.findViewById(R.id.conditions_of_use_header_container).setOnClickListener(v -> {
+ AccessibilityUtil.setExpansionToggleStateAccessibilityDelegate(termsOfUseHeader, termsOfUseContainer);
+ termsOfUseHeader.setOnClickListener(v -> {
if (termsOfUseContainer.getVisibility() == View.VISIBLE) {
termsOfUseContainer.setVisibility(View.GONE);
v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.white));
@@ -82,8 +87,10 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
.start();
});
- dataProtectionToOnlineVersionButton.setOnClickListener(v -> { openOnlineVersion();});
- termsOfUseToOnlineVersionButton.setOnClickListener(v -> { openOnlineVersion();});
+ dataProtectionToOnlineVersionButton.setOnClickListener(v -> openOnlineVersion());
+ AccessibilityUtil.setButtonAccessibilityDelegate(dataProtectionToOnlineVersionButton);
+ termsOfUseToOnlineVersionButton.setOnClickListener(v -> openOnlineVersion());
+ AccessibilityUtil.setButtonAccessibilityDelegate(termsOfUseToOnlineVersionButton);
Button continueButton = view.findViewById(R.id.onboarding_continue_button);
continueButton.setOnClickListener(v -> ((OnboardingActivity) getActivity()).continueToNextPage());
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/AccessibilityUtil.kt b/app/src/main/java/ch/admin/bag/dp3t/util/AccessibilityUtil.kt
new file mode 100644
index 000000000..bac29f5af
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/AccessibilityUtil.kt
@@ -0,0 +1,46 @@
+@file:JvmName("AccessibilityUtil")
+
+package ch.admin.bag.dp3t.util
+
+import android.content.Context
+import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.Button
+import androidx.core.view.doOnLayout
+
+fun Context.isAccessibilityActive(): Boolean {
+ val am = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager?
+ return am != null && am.isEnabled && am.isTouchExplorationEnabled
+}
+
+fun View.requestAccessibilityFocus() {
+ doOnLayout {
+ performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED)
+ }
+}
+
+fun View.setExpansionToggleStateAccessibilityDelegate(expandableView: View) {
+ accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
+ info.addAction(
+ if (expandableView.visibility == View.VISIBLE)
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
+ else
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND
+ )
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ }
+ }
+}
+
+fun View.setButtonAccessibilityDelegate() {
+ accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ info.className = Button::class.java.name
+ }
+ }
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/view/DateTimePicker.kt b/app/src/main/java/ch/admin/bag/dp3t/view/DateTimePicker.kt
index 4c5568e9a..27c39b9ff 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/view/DateTimePicker.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/view/DateTimePicker.kt
@@ -70,8 +70,11 @@ class DateTimePicker @JvmOverloads constructor(
formatter = dateFormatter
typeface = font
setSelectedTypeface(font)
- setOnValueChangedListener { _, _, _ ->
+ setOnValueChangedListener { _, _, value ->
changeListener?.onDateTimeChanged(getSelectedDateTime())
+ post {
+ contentDescription = formatter.format(value)
+ }
}
}
@@ -131,7 +134,10 @@ class DateTimePicker @JvmOverloads constructor(
private fun updatePickerValues() {
val dayDifference = ChronoUnit.DAYS.between(now.toLocalDate(), previousDateTime.toLocalDate())
val preSelectedValue = daysInPast + dayDifference
- binding.datePicker.value = preSelectedValue.toInt()
+ binding.datePicker.apply {
+ value = preSelectedValue.toInt()
+ contentDescription = formatter.format(value)
+ }
binding.hourPicker.value = previousDateTime.hour
val minuteSelectionValue = previousDateTime.minute
binding.minutePicker.value = minuteSelectionValue
diff --git a/app/src/main/res/drawable/ic_check_filled.xml b/app/src/main/res/drawable/ic_check_filled.xml
new file mode 100644
index 000000000..9e0ca4c0c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_check_filled.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml
new file mode 100644
index 000000000..d0525b2d0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml
index 8a57d94db..7e91ac271 100644
--- a/app/src/main/res/layout/activity_onboarding.xml
+++ b/app/src/main/res/layout/activity_onboarding.xml
@@ -14,6 +14,7 @@
diff --git a/app/src/main/res/layout/card_what_to_do_self_isolation.xml b/app/src/main/res/layout/card_what_to_do_self_isolation.xml
index 0cf5b67f5..3467a72d1 100644
--- a/app/src/main/res/layout/card_what_to_do_self_isolation.xml
+++ b/app/src/main/res/layout/card_what_to_do_self_isolation.xml
@@ -93,7 +93,7 @@
android:layout_height="1dp"
android:background="@color/grey_light" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_camera_permission_explanation.xml b/app/src/main/res/layout/dialog_camera_permission_explanation.xml
index 44981bcd6..ec927dc3b 100644
--- a/app/src/main/res/layout/dialog_camera_permission_explanation.xml
+++ b/app/src/main/res/layout/dialog_camera_permission_explanation.xml
@@ -19,8 +19,10 @@
android:layout_gravity="end"
android:layout_marginTop="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
+ android:accessibilityTraversalAfter="@id/camera_permission_dialog_ok_button"
android:background="@drawable/ripple_rounded"
android:clickable="true"
+ android:contentDescription="@string/cancel"
android:focusable="true"
android:padding="@dimen/spacing_medium_large"
android:src="@drawable/ic_close"
@@ -36,16 +38,15 @@
android:gravity="center"
android:text="@string/camera_permission_dialog_text" />
-
diff --git a/app/src/main/res/layout/dialog_fragment_checkout_conflict.xml b/app/src/main/res/layout/dialog_fragment_checkout_conflict.xml
new file mode 100644
index 000000000..85f20e22a
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_checkout_conflict.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_fragment_checkout_conflict_resolved.xml b/app/src/main/res/layout/dialog_fragment_checkout_conflict_resolved.xml
new file mode 100644
index 000000000..27121e785
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_checkout_conflict_resolved.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_fragment_hide_in_diary.xml b/app/src/main/res/layout/dialog_fragment_hide_in_diary.xml
index ab9aee7d1..dc8ec2477 100644
--- a/app/src/main/res/layout/dialog_fragment_hide_in_diary.xml
+++ b/app/src/main/res/layout/dialog_fragment_hide_in_diary.xml
@@ -12,12 +12,13 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
@@ -80,14 +81,13 @@
android:layout_marginBottom="@dimen/spacing_large"
android:text="@string/remove_diary_remove_now_text" />
-
diff --git a/app/src/main/res/layout/dialog_reminder_duration.xml b/app/src/main/res/layout/dialog_reminder_duration.xml
index 55973aedc..a28522007 100644
--- a/app/src/main/res/layout/dialog_reminder_duration.xml
+++ b/app/src/main/res/layout/dialog_reminder_duration.xml
@@ -12,28 +12,26 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
diff --git a/app/src/main/res/layout/fragment_check_in.xml b/app/src/main/res/layout/fragment_check_in.xml
index eb5070e02..f83e45588 100644
--- a/app/src/main/res/layout/fragment_check_in.xml
+++ b/app/src/main/res/layout/fragment_check_in.xml
@@ -16,15 +16,14 @@
android:elevation="@dimen/card_elevation"
android:visibility="gone">
-
diff --git a/app/src/main/res/layout/fragment_check_out_and_edit.xml b/app/src/main/res/layout/fragment_check_out_and_edit.xml
index b36e99bc8..711b965d0 100644
--- a/app/src/main/res/layout/fragment_check_out_and_edit.xml
+++ b/app/src/main/res/layout/fragment_check_out_and_edit.xml
@@ -13,28 +13,26 @@
android:background="@color/white"
android:elevation="@dimen/card_elevation">
-
-
diff --git a/app/src/main/res/layout/fragment_checkin_diary.xml b/app/src/main/res/layout/fragment_checkin_diary.xml
index f28dd5f4e..dc9c0c4cb 100644
--- a/app/src/main/res/layout/fragment_checkin_diary.xml
+++ b/app/src/main/res/layout/fragment_checkin_diary.xml
@@ -33,6 +33,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
+ android:importantForAccessibility="no"
android:orientation="vertical"
android:paddingHorizontal="@dimen/spacing_large"
android:paddingBottom="@dimen/spacing_small"
diff --git a/app/src/main/res/layout/fragment_events_overview.xml b/app/src/main/res/layout/fragment_events_overview.xml
index 6cc4a1ffd..2a08235a3 100644
--- a/app/src/main/res/layout/fragment_events_overview.xml
+++ b/app/src/main/res/layout/fragment_events_overview.xml
@@ -26,6 +26,7 @@
android:id="@+id/qr_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:importantForAccessibility="no"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_generate_qr_code.xml b/app/src/main/res/layout/fragment_generate_qr_code.xml
index 8fb7d683f..d84864235 100644
--- a/app/src/main/res/layout/fragment_generate_qr_code.xml
+++ b/app/src/main/res/layout/fragment_generate_qr_code.xml
@@ -21,15 +21,14 @@
android:background="@color/white"
android:elevation="@dimen/card_elevation">
-
diff --git a/app/src/main/res/layout/fragment_inform.xml b/app/src/main/res/layout/fragment_inform.xml
index 9022b1244..d307f1f0f 100644
--- a/app/src/main/res/layout/fragment_inform.xml
+++ b/app/src/main/res/layout/fragment_inform.xml
@@ -40,6 +40,7 @@
android:orientation="vertical">
-
diff --git a/app/src/main/res/layout/fragment_not_thank_you.xml b/app/src/main/res/layout/fragment_not_thank_you.xml
index f9c12b340..fef5ba257 100644
--- a/app/src/main/res/layout/fragment_not_thank_you.xml
+++ b/app/src/main/res/layout/fragment_not_thank_you.xml
@@ -95,15 +95,14 @@
android:layout_height="wrap_content"
android:text="@string/not_thank_you_screen_back_button" />
-
diff --git a/app/src/main/res/layout/fragment_onboarding_content.xml b/app/src/main/res/layout/fragment_onboarding_content.xml
index cdb38a338..e46a9efbd 100644
--- a/app/src/main/res/layout/fragment_onboarding_content.xml
+++ b/app/src/main/res/layout/fragment_onboarding_content.xml
@@ -34,6 +34,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_very_large"
+ android:accessibilityHeading="true"
android:gravity="center_horizontal"
tools:text="Subtitle" />
diff --git a/app/src/main/res/layout/fragment_onboarding_disclaimer.xml b/app/src/main/res/layout/fragment_onboarding_disclaimer.xml
index 8ba7b1719..6cc69a9fa 100644
--- a/app/src/main/res/layout/fragment_onboarding_disclaimer.xml
+++ b/app/src/main/res/layout/fragment_onboarding_disclaimer.xml
@@ -39,6 +39,7 @@
style="@style/NextStep.Text.Blue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:accessibilityHeading="true"
android:gravity="center_horizontal"
android:text="@string/onboarding_disclaimer_heading" />
diff --git a/app/src/main/res/layout/fragment_onboarding_permission_battery.xml b/app/src/main/res/layout/fragment_onboarding_permission_battery.xml
index 685946d78..9926e9650 100644
--- a/app/src/main/res/layout/fragment_onboarding_permission_battery.xml
+++ b/app/src/main/res/layout/fragment_onboarding_permission_battery.xml
@@ -92,6 +92,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_large"
+ android:accessibilityHeading="true"
android:text="@string/onboarding_good_to_know" />
-
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
index 523d14fd4..c2f84aac7 100644
--- a/app/src/main/res/layout/fragment_qr_code.xml
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -13,9 +13,9 @@
android:background="@color/white"
android:elevation="@dimen/card_elevation">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_events_explanation.xml b/app/src/main/res/layout/item_events_explanation.xml
index b270f50ef..52d7c5955 100644
--- a/app/src/main/res/layout/item_events_explanation.xml
+++ b/app/src/main/res/layout/item_events_explanation.xml
@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:importantForAccessibility="no"
android:orientation="vertical">
+
+
diff --git a/app/src/main/res/layout/item_events_footer.xml b/app/src/main/res/layout/item_events_footer.xml
index 693f5b41c..27e7d344d 100644
--- a/app/src/main/res/layout/item_events_footer.xml
+++ b/app/src/main/res/layout/item_events_footer.xml
@@ -3,6 +3,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_larger"
+ android:importantForAccessibility="no"
android:orientation="vertical"
android:padding="@dimen/spacing_medium_large">
diff --git a/app/src/main/res/layout/item_generate_qr_code.xml b/app/src/main/res/layout/item_generate_qr_code.xml
index 420b33600..fa36d4c63 100644
--- a/app/src/main/res/layout/item_generate_qr_code.xml
+++ b/app/src/main/res/layout/item_generate_qr_code.xml
@@ -4,7 +4,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="@dimen/spacing_very_large"
- android:gravity="center_horizontal">
+ android:gravity="center_horizontal"
+ android:importantForAccessibility="no">
-
diff --git a/common/src/main/res/values-bs/strings.xml b/common/src/main/res/values-bs/strings.xml
index ea3367882..c26576465 100644
--- a/common/src/main/res/values-bs/strings.xml
+++ b/common/src/main/res/values-bs/strings.xml
@@ -1523,7 +1523,7 @@ U tom slučaju kontaktirajte nas ovde:""Greška""QR kod je još nevažeći""QR kod je nevažeći "
- "Upravo ste prijavljeni."
+ "Već se prijavljeni na drugom mestu.""Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje.""Potrebno ažuriranje""Sada ažurirati"
@@ -1799,4 +1799,58 @@ U tom slučaju kontaktirajte nas ovde:"
"Instalirati aplikaciju"
+
+
+ "Skini PDF"
+
+
+ "redukovano"
+
+
+ "prošireno"
+
+
+ "Nije moguće osigurati"
+
+
+ "Odabrano vreme za {CHECKIN} se poklapa sa drugim vremenom prijave. Molimo proverite još jednom vaše unose."
+
+
+ "Nazad"
+
+
+ "Sati"
+
+
+ "Minuta"
+
+
+ "Sat"
+
+
+ "Više ne postoje protivrečnosti kod unetog vremena. Sada se možete odjaviti sa {CHECKIN}."
+
+
+ "QR kod je izrađen"
+
+
+ "Izradi novi QR kod"
+
+
+ "Više informacija o aplikaciji"
+
+
+ "Više informacija o Covid kodu"
+
+
+ "Više informacija o broju slučajeva"
+
+
+ "Izrađeni QR kod"
+
+
+ "Alternativno, možete izraditi QR kod na qr.swisscovid.ch."
+
+
+ "Kako biste se prijavili na nekom drugom mestu, najpre se morate odjaviti odavde."
\ No newline at end of file
diff --git a/common/src/main/res/values-de/strings.xml b/common/src/main/res/values-de/strings.xml
index 8497b9ae7..fe324068b 100644
--- a/common/src/main/res/values-de/strings.xml
+++ b/common/src/main/res/values-de/strings.xml
@@ -1586,7 +1586,7 @@ Dann rufen Sie hier an:"
"Fehler""QR-Code noch nicht gültig""QR-Code nicht mehr gültig"
- "Sie sind bereits eingecheckt."
+ "Sie sind bereits an einem anderen Ort eingecheckt.""Dieser QR-Code ist nicht mit der installierten App-Version kompatibel. Bitte aktualisieren Sie die App, um fortzufahren.""Update erforderlich""Jetzt aktualisieren"
@@ -1882,4 +1882,60 @@ Dieser Titel wird beim Check-in angezeigt."
"App installieren"
+
+
+
+ "PDF herunterladen"
+
+
+ "reduziert"
+
+
+ "erweitert"
+
+
+ "Sichern nicht möglich"
+
+
+ "Ihre gewählten Zeiten für {CHECKIN} überschneiden sich mit anderen Check-ins. Bitte überprüfen Sie nochmals Ihre Angaben."
+
+
+ "Zurück"
+
+
+ "Stunden"
+
+
+ "Minuten"
+
+
+ "Stunde"
+
+
+ "Es gibt keine Konflikte mehr für Ihre eingetragene Zeit. Sie können jetzt bei {CHECKIN} auschecken."
+
+
+ "QR-Code erstellt"
+
+
+ "Neuen QR-Code erstellen"
+
+
+ "Mehr Informationen zur App"
+
+
+ "Mehr Informationen zu Covidcodes"
+
+
+ "Mehr Informationen zu den Fallzahlen"
+
+
+ "Erstellter QR-Code"
+
+
+
+ "Alternativ können Sie QR-Codes auch auf qr.swisscovid.ch erstellen."
+
+
+ "Um sich an einem neuen Ort einzuchecken, müssen Sie sich zuerst auschecken."
\ No newline at end of file
diff --git a/common/src/main/res/values-es/strings.xml b/common/src/main/res/values-es/strings.xml
index 27f3ccdf1..fd0e96c6c 100644
--- a/common/src/main/res/values-es/strings.xml
+++ b/common/src/main/res/values-es/strings.xml
@@ -1515,7 +1515,7 @@ Llame a este número:"
"Error""Código QR aún no válido""Código QR ya no es válido"
- "Ya ha registrado su entrada."
+ "Está usted registrado mediante Check-in en otro lugar.""Este código QR no es compatible con la versión instalada de la app. Actualice la aplicación para continuar.""Se requiere actualización""Actualizar ahora"
@@ -1791,4 +1791,58 @@ Posibles utilizaciones:
"Instalar la aplicación"
+
+
+ "Descargar PDF"
+
+
+ "visión reducida"
+
+
+ "visión ampliada"
+
+
+ "No es posible guardar"
+
+
+ "La hora seleccionada para {CHECKIN} coincide con otros Check-ins. Por favor, controle nuevamente las indicaciones."
+
+
+ "Volver"
+
+
+ "horas"
+
+
+ "minutos"
+
+
+ "hora"
+
+
+ "No hay conflicto para la hora indicada. Puede usted salir de {CHECKIN}."
+
+
+ "Código QR ha sido generado"
+
+
+ "Generar un nuevo código QR"
+
+
+ "Más información sobre la app"
+
+
+ "Más información sobre el código Covid"
+
+
+ "Más información sobre el número de casos"
+
+
+ "Código QR generado"
+
+
+ "También puede usted generar códigos QR en qr.swisscovid.ch."
+
+
+ "Para poder registrarse en una nueva localidad, tiene que primero darse de baja mediante Check-out. "
\ No newline at end of file
diff --git a/common/src/main/res/values-fr/strings.xml b/common/src/main/res/values-fr/strings.xml
index 621a0d73c..6c9ba8ce0 100644
--- a/common/src/main/res/values-fr/strings.xml
+++ b/common/src/main/res/values-fr/strings.xml
@@ -1551,7 +1551,7 @@ Appelez :"
"Erreur""Le code QR n’est pas encore valable.""Le code QR n’est plus valable."
- "Votre check-in a déjà été établie."
+ "Vous êtes déjà enregistré à un autre endroit.""Ce code QR n'est pas compatible avec la version installée de l’application. Veuillez mettre à jour l’application.""Mise à jour requise""Mettre à jour "
@@ -1823,4 +1823,58 @@ Ce titre s’affichera lors du check-in."
"Installer l'application"
+
+
+ "Télécharger le PDF"
+
+
+ "réduit"
+
+
+ "étendu"
+
+
+ "Sauvegarde impossible"
+
+
+ "Les heures que vous avez saisies pour {CHECKIN} sont en conflit avec d'autres enregistrements. Veuillez vérifier à nouveau vos données."
+
+
+ "Retour"
+
+
+ "heures"
+
+
+ "minutes"
+
+
+ "heure"
+
+
+ "Il n'y a plus de conflits pour les heures que vous avez saisies. Vous pouvez maintenant enregistrer votre départ pour {CHECKIN}."
+
+
+ "Code QR créé"
+
+
+ "Créer un nouveau code QR"
+
+
+ "Plus d'informations sur l'application"
+
+
+ "Plus d'informations sur les codes COVID"
+
+
+ "Plus d'informations sur le nombre de cas"
+
+
+ "Code QR créé"
+
+
+ "Vous pouvez également créer des codes QR sur qr.swisscovid.ch."
+
+
+ "Pour vous connecter à un nouvel endroit, vous devez d'abord enregistrer votre départ."
\ No newline at end of file
diff --git a/common/src/main/res/values-hr/strings.xml b/common/src/main/res/values-hr/strings.xml
index 60f899322..4aa1b44dd 100644
--- a/common/src/main/res/values-hr/strings.xml
+++ b/common/src/main/res/values-hr/strings.xml
@@ -1521,7 +1521,7 @@ U tom slučaju kontaktirajte nas ovde:"
"Greška""QR kod je još nevažeći""QR kod je nevažeći "
- "Upravo ste prijavljeni."
+ "Već se prijavljeni na drugom mestu.""Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje.""Potrebno ažuriranje""Sada ažurirati"
@@ -1797,4 +1797,58 @@ U tom slučaju kontaktirajte nas ovde:"
"Instalirati aplikaciju"
+
+
+ "Skini PDF"
+
+
+ "redukovano"
+
+
+ "prošireno"
+
+
+ "Nije moguće osigurati"
+
+
+ "Odabrano vreme za {CHECKIN} se poklapa sa drugim vremenom prijave. Molimo proverite još jednom vaše unose."
+
+
+ "Nazad"
+
+
+ "Sati"
+
+
+ "Minuta"
+
+
+ "Sat"
+
+
+ "Više ne postoje protivrečnosti kod unetog vremena. Sada se možete odjaviti sa {CHECKIN}."
+
+
+ "QR kod je izrađen"
+
+
+ "Izradi novi QR kod"
+
+
+ "Više informacija o aplikaciji"
+
+
+ "Više informacija o Covid kodu"
+
+
+ "Više informacija o broju slučajeva"
+
+
+ "Izrađeni QR kod"
+
+
+ "Alternativno, možete izraditi QR kod na qr.swisscovid.ch."
+
+
+ "Kako biste se prijavili na nekom drugom mestu, najpre se morate odjaviti odavde."
\ No newline at end of file
diff --git a/common/src/main/res/values-it/strings.xml b/common/src/main/res/values-it/strings.xml
index f1f3cbff5..f029a7c67 100644
--- a/common/src/main/res/values-it/strings.xml
+++ b/common/src/main/res/values-it/strings.xml
@@ -1557,7 +1557,7 @@ Chiama il seguente numero:"
"Errore""Codice QR non ancora valido""Codice QR non più valido"
- "Hai già fatto il check-in."
+ "Hai già fatto il check-in altrove.""Questo codice QR non è compatibile con la versione dell'app installata. Aggiorna l'app per continuare.""Aggiornamento necessario""Aggiorna ora"
@@ -1834,4 +1834,58 @@ Possibili luoghi d'impiego:
"Installa l'app"
+
+
+ "Scarica PDF"
+
+
+ "versione compressa"
+
+
+ "versione espansa"
+
+
+ "Impossibile salvare"
+
+
+ "Gli orari da te selezionati per {CHECKIN} si sovrappongono ad altri check-in. Ricontrolla i dati immessi."
+
+
+ "Indietro"
+
+
+ "Ore"
+
+
+ "Minuti"
+
+
+ "Ora"
+
+
+ "Non ci sono più conflitti tra gli orari che hai immesso. Ora puoi fare il check-out da {CHECKIN}."
+
+
+ "Codice QR creato"
+
+
+ "Crea nuovo codice QR"
+
+
+ "Più informazioni sull'app"
+
+
+ "Più informazioni sui codici Covid"
+
+
+ "Più informazioni sul numero di casi"
+
+
+ "Codice QR creato"
+
+
+ "In alternativa, puoi creare i codici QR anche su qr.swisscovid.ch"
+
+
+ "Per fare il check-in altrove, devi prima fare il check-out."
\ No newline at end of file
diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml
index 787b58eec..f9e83f402 100644
--- a/common/src/main/res/values-pt/strings.xml
+++ b/common/src/main/res/values-pt/strings.xml
@@ -1524,7 +1524,7 @@ Ligue para:"
"Erro""O código QR ainda não é válido!""O código QR não é mais válido!"
- "Você já fez check-in."
+ "Já fez check-in noutro lugar.""Este código QR não é compatível com a versão do aplicativo instalada. Por favor, atualize o aplicativo para continuar.""É necessário atualizar.""Atualizar agora"
@@ -1801,4 +1801,58 @@ Este título vai ser indicado no check-in."
"Instalar o aplicativo"
+
+
+ "Baixar o PDF"
+
+
+ "reduzido"
+
+
+ "ampliado"
+
+
+ "Não é possível salvar"
+
+
+ "Os seus períodos de tempo selecionados para {CHECKIN} sobrepõem-se a outros check-ins. Por favor, verifique as suas informações."
+
+
+ "Voltar"
+
+
+ "horas"
+
+
+ "minutos"
+
+
+ "hora"
+
+
+ "Não há mais conflitos para o seu período de tempo introduzido. Agora, você pode fazer check-out em {CHECKIN}."
+
+
+ "Criou um código QR"
+
+
+ "Criar um novo código QR"
+
+
+ "Mais informações sobre o aplicativo"
+
+
+ "Mais informações sobre Covidcodes"
+
+
+ "Mais informações sobre os números de incidentes"
+
+
+ "Código QR criado"
+
+
+ "Alternativamente também pode criar códigos QR em qr.swisscovid.ch."
+
+
+ "Para fazer check-in noutro lugar, primeiro é necessário fazer check-out."
\ No newline at end of file
diff --git a/common/src/main/res/values-rm/strings.xml b/common/src/main/res/values-rm/strings.xml
index a06284fcb..e110273dc 100644
--- a/common/src/main/res/values-rm/strings.xml
+++ b/common/src/main/res/values-rm/strings.xml
@@ -1521,7 +1521,7 @@ Alura telefonai qua:"
"Sbagl""Il code QR n'è anc betg valaivel""Il code QR n'è betg pli valaivel"
- "Vus essas gia As registrada/registrà."
+ "Vus essas gia registrà/registrada en in auter lieu.""Quest code QR n'è betg cumpatibel cun la versiun da l'app installada. Per plaschair actualisar l'app per cuntinuar.""In update è necessari""Actualisar ussa"
@@ -1797,4 +1797,58 @@ Quai pon esser p.ex.:
"Installar l'app"
+
+
+ "Chargiar giu il PDF"
+
+
+ "reducì"
+
+
+ "extendì"
+
+
+ "Betg pussaivel da segirar"
+
+
+ "Vossas uras tschernidas per {CHECKIN} sa cruschan cun auters check-ins. Controllai per plaschair anc ina giada Vossas indicaziuns."
+
+
+ "Enavos"
+
+
+ "uras"
+
+
+ "minutas"
+
+
+ "ura"
+
+
+ "I n'èn avant maun nagins conflicts pli per Vossas uras endatadas. Vus pudais ussa sortir da {CHECKIN}."
+
+
+ "Il code QR è generà"
+
+
+ "Generar in nov code QR"
+
+
+ "Dapli infurmaziuns davart l'app"
+
+
+ "Dapli infurmaziuns davart ils codes covid"
+
+
+ "Dapli infurmaziuns davart ils dumbers da cas"
+
+
+ "Code QR generà"
+
+
+ "Sco alternativa pudais Vus generar ils codes QR er sin qr.swisscovid.ch."
+
+
+ "Per As registrar en in nov lieu stuais Vus l'emprim sortir dal vegl lieu."
\ No newline at end of file
diff --git a/common/src/main/res/values-sq/strings.xml b/common/src/main/res/values-sq/strings.xml
index b1814b968..e0746683a 100644
--- a/common/src/main/res/values-sq/strings.xml
+++ b/common/src/main/res/values-sq/strings.xml
@@ -1524,7 +1524,7 @@ Atëherë na telefononi këtu:"
"Gabim""Kodi QR nuk është ende i vlefshëm""Kodi QR nuk është më i vlefshëm"
- "Ju jeni kontrolluar tashmë."
+ "Ju tashmë jeni regjistruar në një vend tjetër.""Ky kod QR nuk është i pajtueshëm me versionin e aplikacionit të instaluar. Përditësoni aplikacionin për të vazhduar më tej.""Përditësimi i nevojshëm""Përditësoni tani"
@@ -1802,4 +1802,58 @@ Ky titull shfaqet gjatë Check-in."
"Instaloni aplikacionin"
+
+
+ "Shkarkoni PDF"
+
+
+ "E reduktuar"
+
+
+ "E zgjeruar"
+
+
+ "Ruajtja nuk është e mundur"
+
+
+ "Kohët tuaja të zgjedhura për {CHECKIN} përkojnë me Check-in e tjera. Kontrolloni edhe njëherë të dhënat tuaja."
+
+
+ "Kthehu"
+
+
+ "Orë"
+
+
+ "Minuta"
+
+
+ "Orë"
+
+
+ "Nuk ka më konflikte për kohën tuaj të regjistruar. Ju mund të kontrolloheni tani te {CHECKIN}."
+
+
+ "Kodi i krijuar QR"
+
+
+ "Krijoni kodin e ri QR"
+
+
+ "Më shumë informacione për aplikacionin"
+
+
+ "Më shumë informacione për kodin e Covid"
+
+
+ "Më shumë informacione për numrin e rasteve"
+
+
+ "Kodi i krijuar QR"
+
+
+ "Si alternativë mund të krijoni kodin QR edhe në qr.swisscovid.ch."
+
+
+ "Për t’u regjistruar në një vend të ri, së pari duhet të kontrolloheni."
\ No newline at end of file
diff --git a/common/src/main/res/values-sr/strings.xml b/common/src/main/res/values-sr/strings.xml
index f33a54e36..3ff427b76 100644
--- a/common/src/main/res/values-sr/strings.xml
+++ b/common/src/main/res/values-sr/strings.xml
@@ -1521,7 +1521,7 @@ U tom slučaju kontaktirajte nas ovde:"
"Greška""QR kod je još nevažeći""QR kod je nevažeći "
- "Upravo ste prijavljeni."
+ "Već se prijavljeni na drugom mestu.""Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje.""Potrebno ažuriranje""Sada ažurirati"
@@ -1797,4 +1797,58 @@ U tom slučaju kontaktirajte nas ovde:"
"Instalirati aplikaciju"
+
+
+ "Skini PDF"
+
+
+ "redukovano"
+
+
+ "prošireno"
+
+
+ "Nije moguće osigurati"
+
+
+ "Odabrano vreme za {CHECKIN} se poklapa sa drugim vremenom prijave. Molimo proverite još jednom vaše unose."
+
+
+ "Nazad"
+
+
+ "Sati"
+
+
+ "Minuta"
+
+
+ "Sat"
+
+
+ "Više ne postoje protivrečnosti kod unetog vremena. Sada se možete odjaviti sa {CHECKIN}."
+
+
+ "QR kod je izrađen"
+
+
+ "Izradi novi QR kod"
+
+
+ "Više informacija o aplikaciji"
+
+
+ "Više informacija o Covid kodu"
+
+
+ "Više informacija o broju slučajeva"
+
+
+ "Izrađeni QR kod"
+
+
+ "Alternativno, možete izraditi QR kod na qr.swisscovid.ch."
+
+
+ "Kako biste se prijavili na nekom drugom mestu, najpre se morate odjaviti odavde."
\ No newline at end of file
diff --git a/common/src/main/res/values-ti/strings.xml b/common/src/main/res/values-ti/strings.xml
index 903cc54c5..99cd30356 100644
--- a/common/src/main/res/values-ti/strings.xml
+++ b/common/src/main/res/values-ti/strings.xml
@@ -1507,7 +1507,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ጉድለት""QR-ኮድ ገና ብቑዕ ኣይኮነን""ብቕዓት QR-Code ሓሊፉ"
- "ድሮ ኣቲኹም ዲኹም፧"
+ "ድሮ ኣብ ካልእ ቦታ ኣቲኻ ዘለኻ።""እዚ ኮድ QR ምስቲ ዝተተከለ ቨርዝዮን ኤፕ ኣይሰርሕን እዩ። ነቲ ኤፕ ኣሕድስዎ ብኽብረትኩም መታን ክትቕጸሉ።""ምምሕዳስ የድሊ""ሕጂ ምምሕዳስ"
@@ -1785,4 +1785,58 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ኤፕ ምትካል"
+
+
+ "PDF ኣራግፍ"
+
+
+ "ዝነከየ"
+
+
+ "ዝሰፍሐ"
+
+
+ "ክድግፍ ኣይክእልን ኢዩ"
+
+
+ "እቲ ዝመረጽካዮ ግዜታት ን {CHECKIN} ምስ ካልእ ቸክ-ኢን ተመሳቒሉ። ተ ብኽብረትካ ዝርዝር ሓበሬታኻ እንደገና ርአ።"
+
+
+ "ተመለስ"
+
+
+ "ሰዓታት"
+
+
+ "ደቓይቕ"
+
+
+ "ሰዓት"
+
+
+ "ኣብቲ እትምዝገብሉ ግዜ ግጭት ኣይኪህሉን እዩ ። ሕጂ ኣብ {CHECKIN} ክትወጽእ ትኽእል ኢኻ።"
+
+
+ "ኮድ QR ተፈጢሩ"
+
+
+ "ሓድሽ ኮድ QR ምፍጣር"
+
+
+ "ብዛዕባ እቲ ኣፕሊኬሽን ዝያዳ ሓበሬታ"
+
+
+ "ብዛዕባ ኮቪድኮድ ዝያዳ ሓበሬታ"
+
+
+ "ብዛዕባ ብዝሒ ተለበድቲ ዝያዳ ሓበሬታ"
+
+
+ "ዝተፈጥረ QR-ኮድ"
+
+
+ "ብዘይካዚ ኣብ qr.swisscovid.ch ኮድ QR ክትፈጥር ትኽእል ኢኻ።"
+
+
+ "ናብ ካልእ ቦታ ኽትከይድ እንተ ደሊኻ ፈለማ ክትወጽእ (ቸክ-ኣውት ክትገብር) ኣሎካ።"
\ No newline at end of file
diff --git a/common/src/main/res/values-tr/strings.xml b/common/src/main/res/values-tr/strings.xml
index 712bc0705..12af29fc6 100644
--- a/common/src/main/res/values-tr/strings.xml
+++ b/common/src/main/res/values-tr/strings.xml
@@ -1507,7 +1507,7 @@ O halde şu numarayı arayın:"
"Hata""Kare kod henüz geçerli değil""Kare kod artık geçerli değil"
- "Zaten check-in yaptınız."
+ "Şu anda başka bir yerde Check-in yapmış durumdasınız.""Bu kare kod artık kurulu olan uygulama sürümü ile uyumlu değil. Lütfen işleme devam edebilmek için uygulamayı güncelleyin.""Güncelleme gerekiyor""Şimdi güncelleyin"
@@ -1785,4 +1785,58 @@ Bu başlık check-in sırasında gösterilir."
"Uygulamayı kur"
+
+
+ "PDF indir"
+
+
+ "azaltılmış"
+
+
+ "genişletilmiş"
+
+
+ "Kaydedilemiyor"
+
+
+ "Seçmiş olduğunuz saatler {CHECKIN} başka Ckeck-in'ler ile kesişiyor. Lütfen bilgilerinizi yeniden kontrol edin."
+
+
+ "Geri"
+
+
+ "saat"
+
+
+ "dakika"
+
+
+ "saat"
+
+
+ "Girmiş olduğunuz süreler için artık çatışma yok. Şimdi {CHECKIN}'de çıkış yapabilirsiniz."
+
+
+ "Kare kod oluşturuldu"
+
+
+ "Yeni kare kod oluştur"
+
+
+ "Uygulama hakkında daha fazla bilgi"
+
+
+ "Covid kodları hakkında daha fazla bilgi"
+
+
+ "Vaka sayıları hakkında daha fazla bilgi"
+
+
+ "Oluşturulan kare kod"
+
+
+ "Alternatif olarak kare kodları qr.swisscovid.ch sayfasında da oluşturabilirsiniz."
+
+
+ "Yeni bir yere Check-in yapmak için öncelikle Check-out yapmanız gerekir."
\ No newline at end of file
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index 2304f7b59..1763573b1 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -1546,7 +1546,7 @@ If so, call this number:"
"Error""QR code not yet valid""QR code no longer valid"
- "You are already checked in."
+ "You are already checked in at another location.""This QR code is not compatible with the installed version of the app. Please update the app to continue.""Update required""Update now"
@@ -1823,4 +1823,58 @@ Possible locations:
"Install app"
+
+
+ "Download PDF"
+
+
+ "collapsed"
+
+
+ "expanded"
+
+
+ "Cannot save"
+
+
+ "The times you selected for {CHECKIN} overlap with other check-ins. Please check your entries again."
+
+
+ "Back"
+
+
+ "Hours"
+
+
+ "Minutes"
+
+
+ "Hour"
+
+
+ "There are no longer any conflicts for the time you entered. You can now check out of {CHECKIN}."
+
+
+ "QR code generated"
+
+
+ "Generate new QR code"
+
+
+ "More information on the app"
+
+
+ "More information on Covidcodes"
+
+
+ "More information on case numbers"
+
+
+ "Generated QR code"
+
+
+ "Alternatively, you can also generate QR codes at qr.swisscovid.ch."
+
+
+ "To check in to a new location, you first need to check out."
\ No newline at end of file
diff --git a/common/src/main/res/values/styles.xml b/common/src/main/res/values/styles.xml
index 674ae7b0f..e4d82acfe 100644
--- a/common/src/main/res/values/styles.xml
+++ b/common/src/main/res/values/styles.xml
@@ -8,7 +8,7 @@
~ SPDX-License-Identifier: MPL-2.0
-->
-
+
+
+