From 9f08e7ee5fce5324d182d29bd9cfa40c722cd17b Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 27 Apr 2021 12:12:06 +0200
Subject: [PATCH 001/207] build checks on branch feature/v2
---
.github/workflows/android.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index c47ef47fd..f85ac0b82 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -2,9 +2,9 @@ name: Android Check Build
on:
push:
- branches: [ master, develop ]
+ branches: [ master, develop, feature/v2 ]
pull_request:
- branches: [ master, develop ]
+ branches: [ master, develop, feature/v2 ]
jobs:
build:
From 4f88095641840dbab50fe4bb8c55372f8461f916 Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 27 Apr 2021 11:54:33 +0200
Subject: [PATCH 002/207] setup checkin main screen
---
.../dp3t/checkin/CheckinOverviewFragment.java | 32 ++++++
.../ch/admin/bag/dp3t/home/HomeFragment.java | 32 +++++-
app/src/main/res/layout/card_checkin.xml | 49 ++++++++++
.../res/layout/fragment_checkin_overview.xml | 98 +++++++++++++++++++
app/src/main/res/layout/fragment_home.xml | 6 ++
5 files changed, 212 insertions(+), 5 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
create mode 100644 app/src/main/res/layout/card_checkin.xml
create mode 100644 app/src/main/res/layout/fragment_checkin_overview.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
new file mode 100644
index 000000000..c4ed451a0
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
@@ -0,0 +1,32 @@
+package ch.admin.bag.dp3t.checkin;
+
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import ch.admin.bag.dp3t.R;
+
+public class CheckinOverviewFragment extends Fragment {
+
+ public static CheckinOverviewFragment newInstance() {
+ return new CheckinOverviewFragment();
+ }
+
+ public CheckinOverviewFragment() { super(R.layout.fragment_checkin_overview); }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ view.findViewById(R.id.checkin_overview_scan_qr).setOnClickListener(
+ v -> {
+ // TODO
+ });
+
+ view.findViewById(R.id.checkin_overview_history).setOnClickListener(
+ v -> {
+ // TODO
+ });
+ }
+
+}
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 be6cf9748..49ac2848b 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
@@ -43,6 +43,7 @@
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.CheckinOverviewFragment;
import ch.admin.bag.dp3t.contacts.ContactsFragment;
import ch.admin.bag.dp3t.home.model.NotificationState;
import ch.admin.bag.dp3t.home.model.NotificationStateError;
@@ -77,6 +78,7 @@ public class HomeFragment extends Fragment {
private View reportStatusView;
private View reportErrorView;
private View travelCard;
+ private View checkinCard;
private View cardSymptomsFrame;
private View cardTestFrame;
private View cardSymptoms;
@@ -116,6 +118,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
reportStatusView = reportStatusBubble.findViewById(R.id.report_status);
reportErrorView = reportStatusBubble.findViewById(R.id.report_errors);
travelCard = view.findViewById(R.id.card_travel);
+ checkinCard = view.findViewById(R.id.card_checkin);
headerView = view.findViewById(R.id.home_header_view);
scrollView = view.findViewById(R.id.home_scroll_view);
cardSymptoms = view.findViewById(R.id.card_what_to_do_symptoms);
@@ -128,6 +131,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
setupInfobox();
setupTracingView();
setupNotification();
+ setupCheckinCard();
setupTravelCard();
setupWhatToDo();
setupNonProductionHint();
@@ -247,7 +251,7 @@ private void setupTracingView() {
}
private void showContactsFragment() {
- getActivity().getSupportFragmentManager().beginTransaction()
+ requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, ContactsFragment.newInstance())
.addToBackStack(ContactsFragment.class.getCanonicalName())
@@ -255,7 +259,7 @@ private void showContactsFragment() {
}
private void showReportsFragment() {
- getActivity().getSupportFragmentManager().beginTransaction()
+ requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, ReportsFragment.newInstance())
.addToBackStack(ReportsFragment.class.getCanonicalName())
@@ -355,9 +359,27 @@ private boolean isNotificationChannelEnabled(Context context, @Nullable String c
}
}
+ private void setupCheckinCard() {
+ checkinCard.setOnClickListener(
+ v -> requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, CheckinOverviewFragment.newInstance())
+ .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
+ .commit()
+ );
+
+ checkinCard.findViewById(R.id.checkin_scan).setOnClickListener(
+ v -> requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, CheckinOverviewFragment.newInstance())
+ .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
+ .commit()
+ );
+ }
+
private void setupTravelCard() {
travelCard.setOnClickListener(
- v -> getActivity().getSupportFragmentManager().beginTransaction()
+ v -> requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, TravelFragment.newInstance())
.addToBackStack(TravelFragment.class.getCanonicalName())
@@ -376,13 +398,13 @@ private void setupTravelCard() {
private void setupWhatToDo() {
cardSymptoms.setOnClickListener(
- v -> getActivity().getSupportFragmentManager().beginTransaction()
+ v -> requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, WtdSymptomsFragment.newInstance())
.addToBackStack(WtdSymptomsFragment.class.getCanonicalName())
.commit());
cardTest.setOnClickListener(
- v -> getActivity().getSupportFragmentManager().beginTransaction()
+ v -> requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, WtdPositiveTestFragment.newInstance())
.addToBackStack(WtdPositiveTestFragment.class.getCanonicalName())
diff --git a/app/src/main/res/layout/card_checkin.xml b/app/src/main/res/layout/card_checkin.xml
new file mode 100644
index 000000000..3c568fcf2
--- /dev/null
+++ b/app/src/main/res/layout/card_checkin.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
new file mode 100644
index 000000000..dd381d3b6
--- /dev/null
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 6ff944d93..3fd3debde 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -50,6 +50,12 @@
android:visibility="gone"
tools:visibility="visible" />
+
+
Date: Tue, 27 Apr 2021 11:59:28 +0200
Subject: [PATCH 003/207] Include CrowdNotifier Logic (without any Views or
Fragments)
---
app/build.gradle | 33 +++
app/src/main/AndroidManifest.xml | 11 +
.../CrowdNotifierKeyLoadWorker.java | 81 +++++++
.../dp3t/networking/TraceKeysRepository.java | 99 ++++++++
.../networking/models/TraceKeysService.java | 15 ++
.../dp3t/startup/BootCompletedReceiver.java | 41 ++++
.../admin/bag/dp3t/storage/DiaryStorage.java | 124 ++++++++++
.../admin/bag/dp3t/storage/SecureStorage.java | 20 ++
.../bag/dp3t/storage/model/DiaryEntry.java | 52 ++++
.../util/CrowdNotifierReminderHelper.java | 128 ++++++++++
.../bag/dp3t/util/NotificationHelper.java | 175 ++++++++++++++
.../util/NotificationQuickActionReceiver.java | 21 ++
.../ch/admin/bag/dp3t/util/StringUtil.java | 15 ++
.../bag/dp3t/util/VenueInfoExtensions.java | 67 ++++++
.../viewmodel/CrowdNotifierViewModel.java | 196 ++++++++++++++++
.../dp3t/viewmodel/model/CheckInState.java | 57 +++++
.../model/CrowdNotifierErrorState.java | 49 ++++
.../dp3t/viewmodel/model/ReminderOption.java | 49 ++++
app/src/main/proto/Proto.proto | 50 ++++
app/src/main/res/drawable/ic_cam_off.xml | 10 +
app/src/main/res/drawable/ic_error.xml | 18 ++
.../main/res/drawable/ic_illus_caffeteria.xml | 10 +
.../main/res/drawable/ic_illus_canteen.xml | 14 ++
.../res/drawable/ic_illus_checked_in_time.xml | 222 ++++++++++++++++++
app/src/main/res/drawable/ic_illus_gym.xml | 14 ++
.../res/drawable/ic_illus_kitchen_area.xml | 10 +
.../res/drawable/ic_illus_lecture_room.xml | 10 +
.../main/res/drawable/ic_illus_library.xml | 10 +
.../main/res/drawable/ic_illus_meeting.xml | 10 +
.../res/drawable/ic_illus_office_space.xml | 10 +
app/src/main/res/drawable/ic_illus_other.xml | 10 +
.../res/drawable/ic_illus_private_event.xml | 10 +
app/src/main/res/drawable/ic_illus_shop.xml | 10 +
.../main/res/drawable/ic_notification_off.xml | 10 +
app/src/main/res/drawable/ic_snooze.xml | 5 +
app/src/main/res/values/strings.xml | 67 ++++++
build.gradle | 8 +
37 files changed, 1741 insertions(+)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java
create mode 100644 app/src/main/proto/Proto.proto
create mode 100644 app/src/main/res/drawable/ic_cam_off.xml
create mode 100644 app/src/main/res/drawable/ic_error.xml
create mode 100644 app/src/main/res/drawable/ic_illus_caffeteria.xml
create mode 100644 app/src/main/res/drawable/ic_illus_canteen.xml
create mode 100644 app/src/main/res/drawable/ic_illus_checked_in_time.xml
create mode 100644 app/src/main/res/drawable/ic_illus_gym.xml
create mode 100644 app/src/main/res/drawable/ic_illus_kitchen_area.xml
create mode 100644 app/src/main/res/drawable/ic_illus_lecture_room.xml
create mode 100644 app/src/main/res/drawable/ic_illus_library.xml
create mode 100644 app/src/main/res/drawable/ic_illus_meeting.xml
create mode 100644 app/src/main/res/drawable/ic_illus_office_space.xml
create mode 100644 app/src/main/res/drawable/ic_illus_other.xml
create mode 100644 app/src/main/res/drawable/ic_illus_private_event.xml
create mode 100644 app/src/main/res/drawable/ic_illus_shop.xml
create mode 100644 app/src/main/res/drawable/ic_notification_off.xml
create mode 100644 app/src/main/res/drawable/ic_snooze.xml
diff --git a/app/build.gradle b/app/build.gradle
index 310e45ffb..41ce00c72 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,6 +12,8 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'org.sonarqube' version '2.8'
+ id 'com.google.protobuf'
+
}
apply from: 'backend_certs.gradle'
@@ -68,6 +70,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-d.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-d.bfs.admin.ch/"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.dev.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.dev.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.dev'
@@ -79,6 +82,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-t.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-t.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-t.bfs.admin.ch/"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.test.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.test.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.test'
@@ -90,6 +94,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-a.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.abnahme.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.abnahme'
@@ -101,6 +106,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
}
@@ -111,6 +117,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
}
@@ -142,6 +149,27 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
+
+ protobuf {
+ protoc {
+ artifact = 'com.google.protobuf:protoc:3.0.0'
+ }
+ plugins {
+ javalite {
+ artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
+ }
+ }
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ remove java
+ }
+ task.plugins {
+ javalite {}
+ }
+ }
+ }
+ }
}
@@ -156,6 +184,7 @@ sonarqube {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
def dp3t_sdk_version = '2.1.0-dev-4'
devImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version-calibration"
@@ -164,6 +193,9 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
+ implementation 'org.crowdnotifier:crowdnotifier-sdk-android:3.0-dev-56'
+
+
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.fragment:fragment:1.3.2'
@@ -179,6 +211,7 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.gms:play-services-base:17.5.0'
+ implementation 'com.google.protobuf:protobuf-lite:3.0.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1a0acd78f..366e0b4d5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,8 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java b/app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java
new file mode 100644
index 000000000..310975e4b
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java
@@ -0,0 +1,81 @@
+package ch.admin.bag.dp3t.networking;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.work.*;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+import org.crowdnotifier.android.sdk.model.ExposureEvent;
+import org.crowdnotifier.android.sdk.model.ProblematicEventInfo;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.storage.DiaryStorage;
+import ch.admin.bag.dp3t.storage.SecureStorage;
+import ch.admin.bag.dp3t.util.NotificationHelper;
+
+import static ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper.autoCheckoutIfNecessary;
+
+
+public class CrowdNotifierKeyLoadWorker extends Worker {
+
+ public static final String ACTION_NEW_EXPOSURE_NOTIFICATION = BuildConfig.APPLICATION_ID + ".ACTION_NEW_EXPOSURE_NOTIFICATION";
+ private static final String WORK_TAG = "ch.ubique.notifyme.app.network.KeyLoadWorker";
+ private static final int DAYS_TO_KEEP_VENUE_VISITS = 14;
+ private static final int REPEAT_INTERVAL_MINUTES = 120;
+ private static final String LOG_TAG = "KeyLoadWorker";
+
+
+ public static void startKeyLoadWorker(Context context) {
+ Constraints constraints = new Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build();
+
+ PeriodicWorkRequest periodicWorkRequest =
+ new PeriodicWorkRequest.Builder(CrowdNotifierKeyLoadWorker.class, REPEAT_INTERVAL_MINUTES, TimeUnit.MINUTES)
+ .setConstraints(constraints)
+ .build();
+
+ WorkManager workManager = WorkManager.getInstance(context);
+ workManager.enqueueUniquePeriodicWork(WORK_TAG, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);
+ }
+
+
+ public CrowdNotifierKeyLoadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
+ super(context, workerParams);
+ }
+
+ @NonNull
+ @Override
+ public Result doWork() {
+ Log.d(LOG_TAG, "Started KeyLoadWorker");
+ List problematicEventInfos = new TraceKeysRepository(getApplicationContext()).loadTraceKeys();
+ if (problematicEventInfos == null) {
+ Log.d(LOG_TAG, "KeyLoadWorker failure");
+ return Result.retry();
+ }
+ List exposures = CrowdNotifier.checkForMatches(problematicEventInfos, getApplicationContext());
+ if (!exposures.isEmpty()) {
+ for (ExposureEvent exposureEvent : exposures) {
+ NotificationHelper.getInstance(getApplicationContext()).showExposureNotification(exposureEvent.getId());
+ }
+ LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(ACTION_NEW_EXPOSURE_NOTIFICATION));
+ }
+ cleanUpOldData(getApplicationContext());
+ autoCheckoutIfNecessary(getApplicationContext(), SecureStorage.getInstance(getApplicationContext()).getCheckInState());
+
+ Log.d(LOG_TAG, "KeyLoadWorker success");
+ return Result.success();
+ }
+
+ public static void cleanUpOldData(Context context) {
+ CrowdNotifier.cleanUpOldData(context, DAYS_TO_KEEP_VENUE_VISITS);
+ DiaryStorage.getInstance(context).removeEntriesBefore(DAYS_TO_KEEP_VENUE_VISITS);
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java
new file mode 100644
index 000000000..1d00e581e
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java
@@ -0,0 +1,99 @@
+package ch.admin.bag.dp3t.networking;
+
+import android.content.Context;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.crowdnotifier.android.sdk.model.ProblematicEventInfo;
+import org.dpppt.android.sdk.DP3T;
+import org.dpppt.android.sdk.backend.UserAgentInterceptor;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.networking.models.Proto;
+import ch.admin.bag.dp3t.networking.models.TraceKeysService;
+import ch.admin.bag.dp3t.storage.SecureStorage;
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+public class TraceKeysRepository {
+
+ private static final String KEY_BUNDLE_TAG_HEADER = "x-key-bundle-tag";
+
+ private TraceKeysService traceKeysService;
+ private SecureStorage storage;
+
+ public TraceKeysRepository(Context context) {
+
+ storage = SecureStorage.getInstance(context);
+ String baseUrl = BuildConfig.PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL;
+
+ OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
+ okHttpBuilder.networkInterceptors().add(new UserAgentInterceptor(DP3T.getUserAgent()));
+
+ Retrofit bucketRetrofit = new Retrofit.Builder()
+ .baseUrl(baseUrl)
+ .client(okHttpBuilder.build())
+ .build();
+
+ traceKeysService = bucketRetrofit.create(TraceKeysService.class);
+ }
+
+ public void loadTraceKeysAsync(Callback callback) {
+ traceKeysService.getTraceKeys(storage.getCrowdNotifierLastKeyBundleTag()).enqueue(new retrofit2.Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ callback.onTraceKeysLoaded(handleSuccessfulResponse(response));
+ } else {
+ callback.onTraceKeysLoaded(null);
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ callback.onTraceKeysLoaded(null);
+ }
+ });
+ }
+
+ public List loadTraceKeys() {
+ try {
+ Response response = traceKeysService.getTraceKeys(storage.getCrowdNotifierLastKeyBundleTag()).execute();
+ if (response.isSuccessful()) {
+ return handleSuccessfulResponse(response);
+ }
+ } catch (IOException e) {
+ return null;
+ }
+ return null;
+ }
+
+ private List handleSuccessfulResponse(Response response) {
+ try {
+ long keyBundleTag = Long.parseLong(response.headers().get(KEY_BUNDLE_TAG_HEADER));
+ storage.setCrowdNotifierLastKeyBundleTag(keyBundleTag);
+ Proto.ProblematicEventWrapper problematicEventWrapper =
+ Proto.ProblematicEventWrapper.parseFrom(response.body().byteStream());
+ ArrayList problematicEventInfos = new ArrayList<>();
+ for (Proto.ProblematicEvent event : problematicEventWrapper.getEventsList()) {
+ problematicEventInfos.add(new ProblematicEventInfo(event.getIdentity().toByteArray(),
+ event.getSecretKeyForIdentity().toByteArray(), event.getStartTime(), event.getEndTime(),
+ event.getEncryptedAssociatedData().toByteArray(), event.getCipherTextNonce().toByteArray()));
+ }
+ return problematicEventInfos;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public interface Callback {
+ void onTraceKeysLoaded(List traceKeys);
+
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java b/app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java
new file mode 100644
index 000000000..b7d2f657d
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java
@@ -0,0 +1,15 @@
+package ch.admin.bag.dp3t.networking.models;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+import retrofit2.http.Query;
+
+public interface TraceKeysService {
+
+ @Headers("Accept: application/x-protobuf")
+ @GET("v3/traceKeys")
+ Call getTraceKeys(@Query("lastKeyBundleTag") long lastKeyBundleTag);
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java b/app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java
new file mode 100644
index 000000000..312ea98b0
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java
@@ -0,0 +1,41 @@
+package ch.admin.bag.dp3t.startup;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import ch.admin.bag.dp3t.storage.SecureStorage;
+import ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper;
+import ch.admin.bag.dp3t.util.NotificationHelper;
+import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+
+public class BootCompletedReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ return;
+ }
+
+ invalidateCheckIn(context);
+ }
+
+ private void invalidateCheckIn(Context context) {
+ SecureStorage storage = SecureStorage.getInstance(context);
+ CheckInState checkInState = storage.getCheckInState();
+ if (checkInState == null) {
+ return;
+ }
+
+ boolean checkedOut = CrowdNotifierReminderHelper.autoCheckoutIfNecessary(context, checkInState);
+
+ if (!checkedOut) {
+ long checkInTime = checkInState.getCheckInTime();
+ NotificationHelper.getInstance(context).startOngoingNotification(checkInTime, checkInState.getVenueInfo());
+ CrowdNotifierReminderHelper.set8HourReminder(checkInTime, context);
+ CrowdNotifierReminderHelper.setAutoCheckOut(checkInTime, context);
+ CrowdNotifierReminderHelper.setReminder(checkInTime + checkInState.getSelectedTimerOption().getDelayMillis(), context);
+ }
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java
new file mode 100644
index 000000000..823b98e76
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java
@@ -0,0 +1,124 @@
+package ch.admin.bag.dp3t.storage;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import androidx.security.crypto.EncryptedSharedPreferences;
+import androidx.security.crypto.MasterKeys;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+
+import org.crowdnotifier.android.sdk.model.DayDate;
+
+import ch.admin.bag.dp3t.storage.model.DiaryEntry;
+
+public class DiaryStorage {
+
+ private static final String KEY_DIARY_STORE = "KEY_DIARY_STORE";
+ private static final String KEY_DIARY_ENTRIES_V3 = "KEY_DIARY_ENTRIES_V3";
+ private static final Type EXPOSURE_LIST_V3_TYPE = new TypeToken>() { }.getType();
+
+
+ private static DiaryStorage instance;
+
+ private SharedPreferences sharedPreferences;
+ private final Gson gson = new Gson();
+
+ private DiaryStorage(Context context) {
+ try {
+ String KEY_ALIAS = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
+ sharedPreferences = EncryptedSharedPreferences.create(KEY_DIARY_STORE,
+ KEY_ALIAS,
+ context,
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
+ } catch (GeneralSecurityException | IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public static synchronized DiaryStorage getInstance(Context context) {
+ if (instance == null) {
+ instance = new DiaryStorage(context);
+ }
+ return instance;
+ }
+
+ public boolean addEntry(DiaryEntry diaryEntry) {
+ List diaryEntries = getEntries();
+ if (hasExposureWithId(diaryEntry.getId())) return false;
+ diaryEntries.add(diaryEntry);
+ saveToPrefs(diaryEntries);
+ return true;
+ }
+
+ public boolean updateEntry(DiaryEntry newDiaryEntry) {
+ List diaryEntries = getEntries();
+ DiaryEntry oldDiaryEntry = getDiaryEntryWithId(diaryEntries, newDiaryEntry.getId());
+ if (oldDiaryEntry == null) return false;
+ diaryEntries.remove(oldDiaryEntry);
+ diaryEntries.add(newDiaryEntry);
+ saveToPrefs(diaryEntries);
+ return true;
+ }
+
+ public boolean removeEntry(long id) {
+ List diaryEntries = getEntries();
+ DiaryEntry diaryEntry = getDiaryEntryWithId(diaryEntries, id);
+ if (diaryEntry == null) return false;
+ diaryEntries.remove(diaryEntry);
+ saveToPrefs(diaryEntries);
+ return true;
+ }
+
+ public boolean hasExposureWithId(long id) {
+ return getDiaryEntryWithId(id) != null;
+ }
+
+ public DiaryEntry getDiaryEntryWithId(long id) {
+ return getDiaryEntryWithId(getEntries(), id);
+ }
+
+ private DiaryEntry getDiaryEntryWithId(List diaryEntries, long id) {
+ for (DiaryEntry exposure : diaryEntries) {
+ if (exposure.getId() == id) {
+ return exposure;
+ }
+ }
+ return null;
+ }
+
+ public List getEntries() {
+ return gson.fromJson(sharedPreferences.getString(KEY_DIARY_ENTRIES_V3, "[]"), EXPOSURE_LIST_V3_TYPE);
+ }
+
+
+ public void removeEntriesBefore(int maxDaysToKeep) {
+ List exposureList = getEntries();
+ DayDate lastDateToKeep = new DayDate().subtractDays(maxDaysToKeep);
+ Iterator iterator = exposureList.iterator();
+ while (iterator.hasNext()) {
+ if (new DayDate(iterator.next().getDepartureTime()).isBefore(lastDateToKeep)) {
+ iterator.remove();
+ }
+ }
+ saveToPrefs(exposureList);
+ }
+
+ private void saveToPrefs(List diaryEntries) {
+ sharedPreferences.edit().putString(KEY_DIARY_ENTRIES_V3, gson.toJson(diaryEntries)).apply();
+ }
+
+ public void clear() {
+ saveToPrefs(new ArrayList<>());
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
index 7eeadcedc..62358b6c7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
@@ -35,6 +35,7 @@
import ch.admin.bag.dp3t.networking.models.TestLocationModel;
import ch.admin.bag.dp3t.networking.models.WhatToDoPositiveTestTextsCollection;
import ch.admin.bag.dp3t.networking.models.WhatToDoPositiveTestTextsModel;
+import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
public class SecureStorage {
@@ -71,6 +72,8 @@ public class SecureStorage {
private static final String KEY_APP_VERSION_CODE = "app_version_code";
private static final String KEY_SCHEDULED_FAKE_WORKER_NAME = "scheduled_fake_worker_name";
private static final String KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY = "positive_report_oldest_shared_key";
+ private static final String KEY_CURRENT_CHECK_IN = "KEY_CURRENT_CHECK_IN";
+ private static final String KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG = "KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG";
private static SecureStorage instance;
@@ -345,6 +348,23 @@ private synchronized SharedPreferences initializeSharedPreferences(@NonNull Cont
}
}
+ public void setCheckInState(CheckInState checkInState) {
+ prefs.edit().putString(KEY_CURRENT_CHECK_IN, gson.toJson(checkInState)).apply();
+ }
+
+ public CheckInState getCheckInState() {
+ return gson.fromJson(prefs.getString(KEY_CURRENT_CHECK_IN, null), CheckInState.class);
+ }
+
+ public void setCrowdNotifierLastKeyBundleTag(long lastSync) {
+ prefs.edit().putLong(KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG, lastSync).apply();
+ }
+
+ public long getCrowdNotifierLastKeyBundleTag() {
+ return prefs.getLong(KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG, 0);
+ }
+
+
/**
* Create or obtain an encrypted SharedPreferences instance. Note that this method is synchronized because the AndroidX
* Security
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java b/app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java
new file mode 100644
index 000000000..0b79d4876
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java
@@ -0,0 +1,52 @@
+package ch.admin.bag.dp3t.storage.model;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+public class DiaryEntry {
+ private long id;
+ private long arrivalTime;
+ private long departureTime;
+ private VenueInfo venueInfo;
+ private String comment;
+
+ public DiaryEntry(long id, long arrivalTime, long departureTime, VenueInfo venueInfo, String comment) {
+ this.id = id;
+ this.arrivalTime = arrivalTime;
+ this.departureTime = departureTime;
+ this.venueInfo = venueInfo;
+ this.comment = comment;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public long getArrivalTime() {
+ return arrivalTime;
+ }
+
+ public long getDepartureTime() {
+ return departureTime;
+ }
+
+ public VenueInfo getVenueInfo() {
+ return venueInfo;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setArrivalTime(long arrivalTime) {
+ this.arrivalTime = arrivalTime;
+ }
+
+ public void setDepartureTime(long departureTime) {
+ this.departureTime = departureTime;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java b/app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java
new file mode 100644
index 000000000..edfbc996d
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java
@@ -0,0 +1,128 @@
+package ch.admin.bag.dp3t.util;
+
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.storage.DiaryStorage;
+import ch.admin.bag.dp3t.storage.SecureStorage;
+import ch.admin.bag.dp3t.storage.model.DiaryEntry;
+import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+
+public class CrowdNotifierReminderHelper extends BroadcastReceiver {
+
+ public static final String ACTION_DID_AUTO_CHECKOUT = BuildConfig.APPLICATION_ID + ".ACTION_DID_AUTO_CHECKOUT";
+ private static final int REMINDER_INTENT_ID = 12;
+ private static final String ACTION_REMINDER = BuildConfig.APPLICATION_ID + ".ACTION_REMINDER";
+ private static final int EIGHT_HOUR_REMINDER_INTENT_ID = 13;
+ private static final String ACTION_EIGHT_HOUR_REMINDER = BuildConfig.APPLICATION_ID + ".ACTION_EIGHT_HOUR_REMINDER";
+ private static final int AUTO_CHECK_OUT_INTENT_ID = 14;
+ private static final String ACTION_AUTO_CHECKOUT = BuildConfig.APPLICATION_ID + ".ACTION_AUTO_CHECKOUT";
+ private static final long EIGHT_HOURS = 1000L * 60 * 60 * 8;
+ public static final long TWELVE_HOURS = 1000L * 60 * 60 * 12;
+
+ public static void removeAllReminders(Context context) {
+ removeReminder(context);
+ remove8HourReminder(context);
+ removeAutoCheckOut(context);
+ }
+
+ public static void removeReminder(Context context) {
+ PendingIntent pendingIntent = getPendingIntent(context, false);
+ removeReminder(pendingIntent, context);
+ }
+
+ public static void setReminder(long alarmTime, Context context) {
+ PendingIntent pendingIntent = getPendingIntent(context, false);
+ setReminder(alarmTime, pendingIntent, context);
+ }
+
+ public static void set8HourReminder(long checkInTime, Context context) {
+ PendingIntent pendingIntent = getPendingIntent(context, true);
+ setReminder(checkInTime + EIGHT_HOURS, pendingIntent, context);
+ }
+
+ public static void remove8HourReminder(Context context) {
+ PendingIntent pendingIntent = getPendingIntent(context, true);
+ removeReminder(pendingIntent, context);
+ }
+
+ public static void setAutoCheckOut(long checkInTime, Context context) {
+ PendingIntent pendingIntent = getAutoCheckOutPendingIntent(context);
+ setReminder(checkInTime + TWELVE_HOURS, pendingIntent, context);
+ }
+
+ public static void removeAutoCheckOut(Context context) {
+ PendingIntent pendingIntent = getAutoCheckOutPendingIntent(context);
+ removeReminder(pendingIntent, context);
+ }
+
+ private static void setReminder(long alarmTime, PendingIntent pendingIntent, Context context) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ if (alarmTime <= System.currentTimeMillis()) {
+ alarmManager.cancel(pendingIntent);
+ return;
+ }
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent);
+ }
+
+ private static void removeReminder(PendingIntent pendingIntent, Context context) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.cancel(pendingIntent);
+ }
+
+ private static PendingIntent getPendingIntent(Context context, boolean eightHours) {
+ Intent intent = new Intent(context, ReminderHelper.class);
+ if (eightHours) {
+ intent.setAction(ACTION_EIGHT_HOUR_REMINDER);
+ return PendingIntent.getBroadcast(context, EIGHT_HOUR_REMINDER_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ } else {
+ intent.setAction(ACTION_REMINDER);
+ return PendingIntent.getBroadcast(context, REMINDER_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+ }
+
+ private static PendingIntent getAutoCheckOutPendingIntent(Context context) {
+ Intent intent = new Intent(context, ReminderHelper.class);
+ intent.setAction(ACTION_AUTO_CHECKOUT);
+ return PendingIntent.getBroadcast(context, AUTO_CHECK_OUT_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ CheckInState checkInState = SecureStorage.getInstance(context).getCheckInState();
+ if (ACTION_REMINDER.equals(intent.getAction()) ||
+ ACTION_EIGHT_HOUR_REMINDER.equals(intent.getAction()) && checkInState != null) {
+ NotificationHelper.getInstance(context).showReminderNotification();
+ } else if (ACTION_AUTO_CHECKOUT.equals(intent.getAction())) {
+ autoCheckoutIfNecessary(context, checkInState);
+ }
+ }
+
+ public static boolean autoCheckoutIfNecessary(Context context, CheckInState checkInState) {
+ if (checkInState == null || checkInState.getCheckInTime() > System.currentTimeMillis() - TWELVE_HOURS) {
+ return false;
+ }
+
+ NotificationHelper notificationHelper = NotificationHelper.getInstance(context);
+ notificationHelper.stopOngoingNotification();
+ notificationHelper.removeReminderNotification();
+ notificationHelper.showAutoCheckoutNotification();
+ long checkIn = checkInState.getCheckInTime();
+ long checkOut = checkIn + TWELVE_HOURS;
+ long id = CrowdNotifier.addCheckIn(checkIn, checkOut, checkInState.getVenueInfo(), context);
+ DiaryStorage.getInstance(context).addEntry(new DiaryEntry(id, checkIn, checkOut, checkInState.getVenueInfo(), ""));
+ SecureStorage storage = SecureStorage.getInstance(context);
+ storage.setCheckInState(null);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_DID_AUTO_CHECKOUT));
+ return true;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java
new file mode 100644
index 000000000..418539dce
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java
@@ -0,0 +1,175 @@
+package ch.admin.bag.dp3t.util;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.TaskStackBuilder;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.MainActivity;
+import ch.admin.bag.dp3t.R;
+
+import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_HIGH;
+import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW;
+
+public class NotificationHelper {
+
+ public static final String ACTION_EXPOSURE_NOTIFICATION = BuildConfig.APPLICATION_ID + ".EXPOSURE_NOTIFICATION_ACTION";
+ public static final String ACTION_REMINDER_NOTIFICATION = BuildConfig.APPLICATION_ID + ".ACTION_REMINDER_NOTIFICATION";
+ public static final String ACTION_AUTO_CHECKOUT_NOTIFICATION = BuildConfig.APPLICATION_ID +
+ ".ACTION_AUTO_CHECKOUT_NOTIFICATION";
+ public static final String ACTION_ONGOING_NOTIFICATION = BuildConfig.APPLICATION_ID + ".ACTION_ONGOING_NOTIFICATION";
+ public static final String ACTION_CHECK_OUT_NOW = BuildConfig.APPLICATION_ID + ".ACTION_CHECK_OUT_NOW";
+ public static final String ACTION_SNOOZE = BuildConfig.APPLICATION_ID + ".ACTION_SNOOZE";
+
+ public static final String EXPOSURE_ID_EXTRA = "EXPOSURE_ID";
+
+ private final String CHANNEL_ID_EXPOSURE_NOTIFICATION = "ExposureNotificaitons";
+ private final String CHANNEL_ID_REMINDER = "Reminders";
+ private final String CHANNEL_ID_ONGOING_CHECK_IN = "Ongoing Check In";
+
+ private final int ONGOING_NOTIFICATION_ID = -1;
+ private final int REMINDER_NOTIFICATION_ID = -2;
+ private final int AUTO_CHECKOUT_NOTIFICATION_ID = -3;
+
+
+ private static NotificationHelper instance;
+
+ private Context context;
+ private NotificationManager notificationManager;
+
+ private NotificationHelper(Context context) {
+ this.context = context;
+ this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ public static synchronized NotificationHelper getInstance(Context context) {
+ if (instance == null) {
+ instance = new NotificationHelper(context);
+ }
+ return instance;
+ }
+
+ private void createNotificationChannel(String channelId, String channelName, boolean silent, int importance) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
+ if (silent) {
+ channel.setSound(null, null);
+ channel.enableVibration(false);
+ }
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+
+ private PendingIntent createBasicPendingIntent(String notificationAction) {
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.setAction(notificationAction);
+ return TaskStackBuilder.create(context)
+ .addNextIntentWithParentStack(intent)
+ .getPendingIntent(notificationAction.hashCode(), PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private PendingIntent createExposurePendingIntent(long exposureId) {
+ Intent intent = new Intent(context, MainActivity.class);
+ intent.setAction(ACTION_EXPOSURE_NOTIFICATION);
+ intent.putExtra(EXPOSURE_ID_EXTRA, exposureId);
+ intent.setAction(Long.toString(exposureId));
+ return TaskStackBuilder.create(context)
+ .addNextIntentWithParentStack(intent)
+ .getPendingIntent(ACTION_EXPOSURE_NOTIFICATION.hashCode(), PendingIntent.FLAG_ONE_SHOT);
+ }
+
+ private NotificationCompat.Builder getNotificationBuilder(String channelId) {
+ return new NotificationCompat.Builder(context, channelId)
+ .setAutoCancel(true)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setDefaults(Notification.DEFAULT_ALL);
+ }
+
+ public void showAutoCheckoutNotification() {
+ createNotificationChannel(CHANNEL_ID_REMINDER, context.getString(R.string.android_reminder_channel_name), false,
+ IMPORTANCE_HIGH);
+
+ Notification notification = getNotificationBuilder(CHANNEL_ID_REMINDER)
+ .setContentIntent(createBasicPendingIntent(ACTION_AUTO_CHECKOUT_NOTIFICATION))
+ .setContentTitle(context.getString(R.string.auto_checkout_title))
+ .setContentText(context.getString(R.string.auto_checkout_body))
+ .build();
+
+ notificationManager.notify(AUTO_CHECKOUT_NOTIFICATION_ID, notification);
+ }
+
+ public void showExposureNotification(long exposureId) {
+
+ createNotificationChannel(CHANNEL_ID_EXPOSURE_NOTIFICATION,
+ context.getString(R.string.android_crowdnotifier_notification_channel_name), false, IMPORTANCE_HIGH);
+
+ Notification notification = getNotificationBuilder(CHANNEL_ID_EXPOSURE_NOTIFICATION)
+ .setContentIntent(createExposurePendingIntent(exposureId))
+ .setContentTitle(context.getString(R.string.exposure_notification_title))
+ .setContentText(context.getString(R.string.exposure_notification_body))
+ .build();
+
+ notificationManager.notify((int) exposureId, notification);
+ }
+
+ public void showReminderNotification() {
+
+ createNotificationChannel(CHANNEL_ID_REMINDER, context.getString(R.string.android_reminder_channel_name), false,
+ IMPORTANCE_HIGH);
+
+ Intent snoozeIntent = new Intent(context, NotificationQuickActionReceiver.class);
+ snoozeIntent.setAction(ACTION_SNOOZE);
+ PendingIntent snoozePendingIntent = PendingIntent.getBroadcast(context, 1, snoozeIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ Notification notification = getNotificationBuilder(CHANNEL_ID_REMINDER)
+ .setContentIntent(createBasicPendingIntent(ACTION_REMINDER_NOTIFICATION))
+ .setContentTitle(context.getString(R.string.checkout_reminder_title))
+ .setContentText(context.getString(R.string.checkout_reminder_text))
+ .addAction(R.drawable.ic_close,
+ context.getString(R.string.ongoing_notification_checkout_quick_action),
+ createBasicPendingIntent(ACTION_CHECK_OUT_NOW))
+ .addAction(R.drawable.ic_snooze,
+ context.getString(R.string.reminder_notification_snooze_action),
+ snoozePendingIntent)
+ .build();
+
+ notificationManager.notify(REMINDER_NOTIFICATION_ID, notification);
+ }
+
+ public void removeReminderNotification() {
+ notificationManager.cancel(REMINDER_NOTIFICATION_ID);
+ }
+
+ public void startOngoingNotification(long startTime, VenueInfo venueInfo) {
+ createNotificationChannel(CHANNEL_ID_ONGOING_CHECK_IN,
+ context.getString(R.string.android_ongoing_checkin_notification_channel_name), true, IMPORTANCE_LOW);
+ Notification ongoingNotification = new NotificationCompat.Builder(context, CHANNEL_ID_ONGOING_CHECK_IN)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle(context.getString(R.string.ongoing_notification_title)
+ .replace("{TIME}", StringUtil.getHourMinuteTimeString(startTime, ":")))
+ .setContentText(venueInfo.getTitle() + "\n" + VenueInfoExtensions.getSubtitle(venueInfo))
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setOngoing(true)
+ .addAction(R.drawable.ic_close,
+ context.getString(R.string.ongoing_notification_checkout_quick_action),
+ createBasicPendingIntent(ACTION_CHECK_OUT_NOW))
+ .setContentIntent(createBasicPendingIntent(ACTION_ONGOING_NOTIFICATION))
+ .build();
+ notificationManager.notify(ONGOING_NOTIFICATION_ID, ongoingNotification);
+ }
+
+ public void stopOngoingNotification() {
+ notificationManager.cancel(ONGOING_NOTIFICATION_ID);
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java
new file mode 100644
index 000000000..7f4c1d7cd
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java
@@ -0,0 +1,21 @@
+package ch.admin.bag.dp3t.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import static ch.admin.bag.dp3t.util.NotificationHelper.ACTION_SNOOZE;
+
+public class NotificationQuickActionReceiver extends BroadcastReceiver {
+
+ private final long SNOOZE_DURATION = 1000L * 60 * 30; // 30 minutes
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_SNOOZE.equals(intent.getAction())) {
+ NotificationHelper.getInstance(context).removeReminderNotification();
+ CrowdNotifierReminderHelper.setReminder(System.currentTimeMillis() + SNOOZE_DURATION, context);
+ }
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
index 2d6491f6d..3a835771e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
@@ -18,6 +18,7 @@
import androidx.annotation.NonNull;
import java.math.BigInteger;
+import java.util.Calendar;
public class StringUtil {
@@ -53,4 +54,18 @@ public static String toHex(byte[] array) {
return hex;
}
+ public static String getHourMinuteTimeString(long timeStamp, String delimiter) {
+ Calendar time = Calendar.getInstance();
+ time.setTimeInMillis(timeStamp);
+ return prependZero(time.get(Calendar.HOUR_OF_DAY)) + delimiter + prependZero(time.get(Calendar.MINUTE));
+ }
+
+ private static String prependZero(int timeUnit) {
+ if (timeUnit < 10) {
+ return "0" + timeUnit;
+ } else {
+ return String.valueOf(timeUnit);
+ }
+ }
+
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java b/app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java
new file mode 100644
index 000000000..97c8642a0
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java
@@ -0,0 +1,67 @@
+package ch.admin.bag.dp3t.util;
+
+import androidx.annotation.DrawableRes;
+
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.networking.models.Proto;
+
+public class VenueInfoExtensions {
+
+
+ public static Proto.SwissCovidLocationData getNotifyMeLocationData(VenueInfo venueInfo) {
+ if (venueInfo.getCountryData() == null) return Proto.SwissCovidLocationData.newBuilder().build();
+ try {
+ return Proto.SwissCovidLocationData.parseFrom(venueInfo.getCountryData());
+ } catch (InvalidProtocolBufferException e) {
+ return Proto.SwissCovidLocationData.newBuilder().build();
+ }
+ }
+
+ public static String getSubtitle(VenueInfo venueInfo) {
+ Proto.SwissCovidLocationData notifyMeLocationData = getNotifyMeLocationData(venueInfo);
+ if (notifyMeLocationData.getRoom() == null || notifyMeLocationData.getRoom().equals("")) {
+ return venueInfo.getAddress();
+ } else {
+ return venueInfo.getAddress() + ", " + notifyMeLocationData.getRoom();
+ }
+ }
+
+ @DrawableRes
+ public static int getVenueTypeDrawable(VenueInfo venueInfo) {
+ return getDrawableForVenueType(getNotifyMeLocationData(venueInfo).getType());
+ }
+
+ @DrawableRes
+ public static int getDrawableForVenueType(Proto.VenueType venueType) {
+ switch (venueType) {
+ case CAFETERIA:
+ return R.drawable.ic_illus_caffeteria;
+ case MEETING_ROOM:
+ return R.drawable.ic_illus_meeting;
+ case PRIVATE_EVENT:
+ return R.drawable.ic_illus_private_event;
+ case CANTEEN:
+ return R.drawable.ic_illus_canteen;
+ case LIBRARY:
+ return R.drawable.ic_illus_library;
+ case LECTURE_ROOM:
+ return R.drawable.ic_illus_lecture_room;
+ case SHOP:
+ return R.drawable.ic_illus_shop;
+ case GYM:
+ return R.drawable.ic_illus_gym;
+ case KITCHEN_AREA:
+ return R.drawable.ic_illus_kitchen_area;
+ case OFFICE_SPACE:
+ return R.drawable.ic_illus_office_space;
+ default:
+ return R.drawable.ic_illus_other;
+ }
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java
new file mode 100644
index 000000000..645cf7dfd
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java
@@ -0,0 +1,196 @@
+package ch.admin.bag.dp3t.viewmodel;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationManagerCompat;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+import org.crowdnotifier.android.sdk.model.ExposureEvent;
+
+import ch.admin.bag.dp3t.networking.TraceKeysRepository;
+import ch.admin.bag.dp3t.storage.SecureStorage;
+import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+import ch.admin.bag.dp3t.viewmodel.model.CrowdNotifierErrorState;
+import ch.admin.bag.dp3t.viewmodel.model.ReminderOption;
+
+import static ch.admin.bag.dp3t.networking.CrowdNotifierKeyLoadWorker.ACTION_NEW_EXPOSURE_NOTIFICATION;
+import static ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper.ACTION_DID_AUTO_CHECKOUT;
+
+public class CrowdNotifierViewModel extends AndroidViewModel {
+
+
+ private final MutableLiveData> exposures = new MutableLiveData<>();
+ private final MutableLiveData timeSinceCheckIn = new MutableLiveData<>(0L);
+ private final MutableLiveData traceKeyLoadingState = new MutableLiveData<>(LoadingState.SUCCESS);
+ private final MutableLiveData errorState = new MutableLiveData<>(null);
+ private final MutableLiveData isCheckedIn = new MutableLiveData<>(false);
+ private CheckInState checkInState;
+
+ private SecureStorage storage;
+ private final Handler handler = new Handler(Looper.getMainLooper());
+ private Runnable timeUpdateRunnable;
+ private final long CHECK_IN_TIME_UPDATE_INTERVAL = 1000;
+ private TraceKeysRepository traceKeysRepository = new TraceKeysRepository(getApplication());
+
+ private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_DID_AUTO_CHECKOUT.equals(intent.getAction())) {
+ setCheckInState(null);
+ } else if (ACTION_NEW_EXPOSURE_NOTIFICATION.equals(intent.getAction())) {
+ refreshExposures();
+ }
+ }
+ };
+
+ public CrowdNotifierViewModel(@NonNull Application application) {
+ super(application);
+ storage = SecureStorage.getInstance(getApplication());
+ checkInState = storage.getCheckInState();
+ updateCheckedIn();
+ LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(application);
+ localBroadcastManager.registerReceiver(broadcastReceiver, new IntentFilter(ACTION_DID_AUTO_CHECKOUT));
+ localBroadcastManager.registerReceiver(broadcastReceiver, new IntentFilter(ACTION_NEW_EXPOSURE_NOTIFICATION));
+ traceKeyLoadingState.observeForever(loadingState -> { if (loadingState != LoadingState.LOADING) refreshErrors(); });
+ }
+
+
+ public void startCheckInTimer() {
+ handler.removeCallbacks(timeUpdateRunnable);
+ timeUpdateRunnable = () -> {
+ if (checkInState != null) {
+ timeSinceCheckIn.setValue(System.currentTimeMillis() - checkInState.getCheckInTime());
+ } else {
+ timeSinceCheckIn.setValue(0L);
+ }
+ handler.postDelayed(timeUpdateRunnable, CHECK_IN_TIME_UPDATE_INTERVAL);
+ };
+ handler.postDelayed(timeUpdateRunnable, CHECK_IN_TIME_UPDATE_INTERVAL -
+ (System.currentTimeMillis() - checkInState.getCheckInTime() % CHECK_IN_TIME_UPDATE_INTERVAL));
+ timeSinceCheckIn.setValue(System.currentTimeMillis() - checkInState.getCheckInTime());
+ }
+
+ public void setCheckInState(CheckInState checkInState) {
+ storage.setCheckInState(checkInState);
+ this.checkInState = checkInState;
+ updateCheckedIn();
+ }
+
+ public CheckInState getCheckInState() {
+ return checkInState;
+ }
+
+ public void setCheckedIn(boolean checkedIn) {
+ if (checkInState != null) checkInState.setCheckedIn(checkedIn);
+ setCheckInState(checkInState);
+ }
+
+ public LiveData> getExposures() {
+ return exposures;
+ }
+
+ public LiveData getTimeSinceCheckIn() {
+ return timeSinceCheckIn;
+ }
+
+ public LiveData getTraceKeyLoadingState() {
+ return traceKeyLoadingState;
+ }
+
+ public LiveData getErrorState() {
+ return errorState;
+ }
+
+ public LiveData isCheckedIn() {
+ return isCheckedIn;
+ }
+
+ public void refreshTraceKeys() {
+ traceKeyLoadingState.setValue(LoadingState.LOADING);
+ traceKeysRepository.loadTraceKeysAsync(traceKeys -> {
+ if (traceKeys == null) {
+ traceKeyLoadingState.setValue(LoadingState.FAILURE);
+ } else {
+ CrowdNotifier.checkForMatches(traceKeys, getApplication());
+ refreshExposures();
+ traceKeyLoadingState.setValue(LoadingState.SUCCESS);
+ }
+ });
+ }
+
+ public void refreshErrors() {
+ //TODO: Also check for disabled notification channels
+ boolean notificationsEnabled = NotificationManagerCompat.from(getApplication()).areNotificationsEnabled();
+
+ if (traceKeyLoadingState.getValue() == LoadingState.FAILURE) {
+ errorState.setValue(CrowdNotifierErrorState.NETWORK);
+ } else if (!notificationsEnabled) {
+ errorState.setValue(CrowdNotifierErrorState.NOTIFICATIONS_DISABLED);
+ } else {
+ errorState.setValue(null);
+ }
+ }
+
+ private void updateCheckedIn() {
+ if (checkInState == null) {
+ isCheckedIn.setValue(false);
+ } else {
+ isCheckedIn.setValue(checkInState.isCheckedIn());
+ }
+ }
+
+ public void refreshExposures() {
+ List newExposures = CrowdNotifier.getExposureEvents(getApplication());
+ Collections.sort(newExposures, (e1, e2) -> Long.compare(e2.getStartTime(), e1.getStartTime()));
+ exposures.setValue(newExposures);
+ }
+
+ public void removeExposure(long exposureId) {
+ CrowdNotifier.removeExposure(getApplication(), exposureId);
+ refreshExposures();
+ }
+
+ public ExposureEvent getExposureWithId(long id) {
+ List exposureEvents = exposures.getValue();
+ if (exposureEvents == null) return null;
+ for (ExposureEvent exposureEvent : exposureEvents) {
+ if (exposureEvent.getId() == id) {
+ return exposureEvent;
+ }
+ }
+ return null;
+ }
+
+ public ReminderOption getSelectedReminderOption() {
+ return checkInState.getSelectedTimerOption();
+ }
+
+ public void setSelectedReminderOption(ReminderOption selectedReminderOption) {
+ this.checkInState.setSelectedTimerOption(selectedReminderOption);
+ storage.setCheckInState(checkInState);
+ }
+
+ @Override
+ public void onCleared() {
+ super.onCleared();
+ LocalBroadcastManager.getInstance(getApplication()).unregisterReceiver(broadcastReceiver);
+ }
+
+ public enum LoadingState {
+ LOADING, SUCCESS, FAILURE
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java
new file mode 100644
index 000000000..7ae8886b9
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java
@@ -0,0 +1,57 @@
+package ch.admin.bag.dp3t.viewmodel.model;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+public class CheckInState {
+ private boolean isCheckedIn;
+ private VenueInfo venueInfo;
+ private long checkInTime;
+ private long checkOutTime;
+ ReminderOption selectedTimerOption;
+
+ public CheckInState(boolean isCheckedIn, VenueInfo venueInfo, long checkInTime, long checkOutTime,
+ ReminderOption selectedTimerOption) {
+ this.isCheckedIn = isCheckedIn;
+ this.venueInfo = venueInfo;
+ this.checkInTime = checkInTime;
+ this.checkOutTime = checkOutTime;
+ this.selectedTimerOption = selectedTimerOption;
+ }
+
+ public boolean isCheckedIn() {
+ return isCheckedIn;
+ }
+
+ public VenueInfo getVenueInfo() {
+ return venueInfo;
+ }
+
+ public long getCheckInTime() {
+ return checkInTime;
+ }
+
+ public long getCheckOutTime() {
+ return checkOutTime;
+ }
+
+ public ReminderOption getSelectedTimerOption() {
+ return selectedTimerOption;
+ }
+
+ public void setCheckedIn(boolean checkedIn) {
+ isCheckedIn = checkedIn;
+ }
+
+ public void setCheckInTime(long checkInTime) {
+ this.checkInTime = checkInTime;
+ }
+
+ public void setCheckOutTime(long checkOutTime) {
+ this.checkOutTime = checkOutTime;
+ }
+
+ public void setSelectedTimerOption(ReminderOption selectedTimerOption) {
+ this.selectedTimerOption = selectedTimerOption;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java
new file mode 100644
index 000000000..3d4013f3e
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java
@@ -0,0 +1,49 @@
+package ch.admin.bag.dp3t.viewmodel.model;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+
+import ch.admin.bag.dp3t.R;
+
+public enum CrowdNotifierErrorState {
+ NETWORK(R.string.error_network_title, R.string.error_network_text, R.string.error_action_retry, R.drawable.ic_error),
+ NOTIFICATIONS_DISABLED(R.string.error_notification_deactivated_title, R.string.error_notification_deactivated_text,
+ R.string.error_action_change_settings, R.drawable.ic_notification_off),
+ CAMERA_ACCESS_DENIED(R.string.error_camera_permission_title, R.string.error_camera_permission_text,
+ R.string.error_action_change_settings, R.drawable.ic_cam_off),
+ NO_VALID_QR_CODE(R.string.error_title, R.string.qrscanner_error, R.string.android_button_ok, R.drawable.ic_error),
+ QR_CODE_NOT_YET_VALID(R.string.error_title, R.string.qr_scanner_error_code_not_yet_valid, R.string.android_button_ok,
+ R.drawable.ic_error),
+ QR_CODE_NOT_VALID_ANYMORE(R.string.error_title, R.string.qr_scanner_error_code_not_valid_anymore, R.string.android_button_ok,
+ R.drawable.ic_error),
+ ALREADY_CHECKED_IN(R.string.error_title, R.string.error_already_checked_in, R.string.android_button_ok, R.drawable.ic_error);
+
+
+ @StringRes private int titleResId;
+ @StringRes private int textResId;
+ @StringRes private int actionResId;
+ @DrawableRes private int imageResId;
+
+ CrowdNotifierErrorState(@StringRes int titleResId, @StringRes int textResId, @StringRes int actionResId, @DrawableRes int imageResId) {
+ this.titleResId = titleResId;
+ this.textResId = textResId;
+ this.actionResId = actionResId;
+ this.imageResId = imageResId;
+ }
+
+ public int getTitleResId() {
+ return titleResId;
+ }
+
+ public int getTextResId() {
+ return textResId;
+ }
+
+ public int getActionResId() {
+ return actionResId;
+ }
+
+ public int getImageResId() {
+ return imageResId;
+ }
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java
new file mode 100644
index 000000000..24e1df12b
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java
@@ -0,0 +1,49 @@
+package ch.admin.bag.dp3t.viewmodel.model;
+
+import android.content.Context;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.R;
+
+
+public enum ReminderOption {
+
+ OFF(0),
+ THIRTY_MINUTES(BuildConfig.FLAVOR.equals("dev") ? 1 : 30),
+ ONE_HOUR(60),
+ TWO_HOURS(120),
+ FOUR_HOURS(240);
+
+ private static final long MINUTE_IN_MILLIS = 60 * 1000L;
+
+ private int delayMinutes;
+ private String name;
+
+ ReminderOption(int delayMinutes) {
+ this.delayMinutes = delayMinutes;
+ this.name = name;
+ }
+
+ public int getDelayMinutes() {
+ return delayMinutes;
+ }
+
+ public long getDelayMillis() {
+ return delayMinutes * MINUTE_IN_MILLIS;
+ }
+
+ public int getDelayHours() {
+ return delayMinutes / 60;
+ }
+
+ public String getName(Context context) {
+ switch (this) {
+ case THIRTY_MINUTES:
+ return context.getString(R.string.reminder_option_minutes).replace("{MINUTES}", String.valueOf(getDelayMinutes()));
+ case OFF:
+ return context.getString(R.string.reminder_option_off);
+ default:
+ return context.getString(R.string.reminder_option_hours).replace("{HOURS}", String.valueOf(getDelayHours()));
+ }
+ }
+}
diff --git a/app/src/main/proto/Proto.proto b/app/src/main/proto/Proto.proto
new file mode 100644
index 000000000..6277753ca
--- /dev/null
+++ b/app/src/main/proto/Proto.proto
@@ -0,0 +1,50 @@
+syntax = "proto3";
+package ch.admin.bag.dp3t.networking.models;
+
+message ProblematicEventWrapper {
+ int32 version = 1;
+ repeated ProblematicEvent events = 2;
+}
+
+message ProblematicEvent {
+ int32 version = 1;
+ bytes identity = 2;
+ bytes secretKeyForIdentity = 3;
+ int64 startTime = 4;
+ int64 endTime = 5;
+ bytes encryptedAssociatedData = 6;
+ bytes cipherTextNonce = 7;
+}
+message AssociatedData {
+ int32 version = 1;
+ string message = 2;
+ bytes countryData = 3;
+}
+message NotifyMeAssociatedData {
+ int32 version = 1;
+ EventCriticality criticality = 2;
+}
+enum EventCriticality {
+ LOW = 0;
+ HIGH = 1;
+}
+
+message SwissCovidLocationData {
+ uint32 version = 1;
+ VenueType type = 2;
+ string room = 3;
+}
+
+enum VenueType {
+ OTHER = 0;
+ MEETING_ROOM = 1;
+ CAFETERIA = 2;
+ PRIVATE_EVENT = 3;
+ CANTEEN = 4;
+ LIBRARY = 5;
+ LECTURE_ROOM = 6;
+ SHOP = 7;
+ GYM = 8;
+ KITCHEN_AREA = 9;
+ OFFICE_SPACE = 10;
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_cam_off.xml b/app/src/main/res/drawable/ic_cam_off.xml
new file mode 100644
index 000000000..4fd131ab6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cam_off.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_error.xml b/app/src/main/res/drawable/ic_error.xml
new file mode 100644
index 000000000..f2f8aacc6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_error.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_caffeteria.xml b/app/src/main/res/drawable/ic_illus_caffeteria.xml
new file mode 100644
index 000000000..df643ae3e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_caffeteria.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_canteen.xml b/app/src/main/res/drawable/ic_illus_canteen.xml
new file mode 100644
index 000000000..6b51511e3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_canteen.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_checked_in_time.xml b/app/src/main/res/drawable/ic_illus_checked_in_time.xml
new file mode 100644
index 000000000..d269922f6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_checked_in_time.xml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_gym.xml b/app/src/main/res/drawable/ic_illus_gym.xml
new file mode 100644
index 000000000..71082ede2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_gym.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_kitchen_area.xml b/app/src/main/res/drawable/ic_illus_kitchen_area.xml
new file mode 100644
index 000000000..e69f5c9aa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_kitchen_area.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_lecture_room.xml b/app/src/main/res/drawable/ic_illus_lecture_room.xml
new file mode 100644
index 000000000..ea50362ee
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_lecture_room.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_library.xml b/app/src/main/res/drawable/ic_illus_library.xml
new file mode 100644
index 000000000..f1d26f625
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_library.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_meeting.xml b/app/src/main/res/drawable/ic_illus_meeting.xml
new file mode 100644
index 000000000..facb30b6c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_meeting.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_office_space.xml b/app/src/main/res/drawable/ic_illus_office_space.xml
new file mode 100644
index 000000000..973dafea0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_office_space.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_other.xml b/app/src/main/res/drawable/ic_illus_other.xml
new file mode 100644
index 000000000..148291144
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_other.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_private_event.xml b/app/src/main/res/drawable/ic_illus_private_event.xml
new file mode 100644
index 000000000..ec124b779
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_private_event.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_illus_shop.xml b/app/src/main/res/drawable/ic_illus_shop.xml
new file mode 100644
index 000000000..b29bc4209
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illus_shop.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notification_off.xml b/app/src/main/res/drawable/ic_notification_off.xml
new file mode 100644
index 000000000..f043d43f5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notification_off.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_snooze.xml b/app/src/main/res/drawable/ic_snooze.xml
new file mode 100644
index 000000000..a86579ee0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_snooze.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ccf0b1c4f..4f42fccc9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1436,4 +1436,71 @@ Contact the Covidcode Infoline:"
+
+
+ "off"
+
+
+ "{HOURS} h"
+
+
+ "{MINUTES}'"
+
+ "Connection error"
+ "Check your internet connection"
+ "Deactivate notifications"
+ "Edit the app settings to receive report-notifications."
+ "Background updates are off"
+ "Edit the app settings to be informed at all times."
+ "No camera access"
+ "The app needs camera access to scan the QR code."
+ "Edit settings"
+ "Try again"
+
+
+ "Error"
+
+
+ "You are already checked in."
+
+ "Code is not valid yet"
+ "Code is not valid anymore"
+
+
+
+ "No valid code!"
+
+
+ "Automated checkout"
+
+
+ "You were checked out automatically."
+
+
+ "New message"
+
+
+ "Open the app to read the message"
+
+
+ "Reports"
+
+
+ "Check-Out Reminder"
+
+
+ "Do you want to check out?"
+
+
+ "Ongoing Check-in Notification"
+
+
+ "You are checked in since {TIME}."
+
+
+ "Check out"
+
+
+ "Snooze"
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 5d08bb591..d730aeb42 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,13 +21,21 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
}
}
allprojects {
repositories {
google()
+ jcenter()
mavenCentral()
+ maven {
+ url "https://dl.bintray.com/terl/lazysodium-maven"
+ }
+ maven {
+ url "https://dl.bintray.com/crowdnotifier/crowdnotifier-sdk-android"
+ }
}
}
From 5035cdb095e64df76df6afed50c99b8b847e81e8 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 28 Apr 2021 08:41:26 +0200
Subject: [PATCH 004/207] Reorder package structure
---
app/src/main/AndroidManifest.xml | 6 +++---
.../CrowdNotifierViewModel.java | 14 +++++++-------
.../model => checkin/models}/CheckInState.java | 2 +-
.../models}/CrowdNotifierErrorState.java | 2 +-
.../model => checkin/models}/DiaryEntry.java | 2 +-
.../model => checkin/models}/ReminderOption.java | 2 +-
.../networking/CrowdNotifierKeyLoadWorker.java | 8 ++++----
.../networking/TraceKeysRepository.java | 5 ++---
.../networking}/TraceKeysService.java | 2 +-
.../startup/BootCompletedReceiver.java | 8 ++++----
.../dp3t/{ => checkin}/storage/DiaryStorage.java | 9 ++++-----
.../utils}/CrowdNotifierReminderHelper.java | 9 +++++----
.../utils}/NotificationHelper.java | 3 ++-
.../utils}/NotificationQuickActionReceiver.java | 4 ++--
.../utils}/VenueInfoExtensions.java | 4 ++--
.../ch/admin/bag/dp3t/storage/SecureStorage.java | 2 +-
app/src/main/proto/Proto.proto | 2 +-
17 files changed, 42 insertions(+), 42 deletions(-)
rename app/src/main/java/ch/admin/bag/dp3t/{viewmodel => checkin}/CrowdNotifierViewModel.java (92%)
rename app/src/main/java/ch/admin/bag/dp3t/{viewmodel/model => checkin/models}/CheckInState.java (96%)
rename app/src/main/java/ch/admin/bag/dp3t/{viewmodel/model => checkin/models}/CrowdNotifierErrorState.java (97%)
rename app/src/main/java/ch/admin/bag/dp3t/{storage/model => checkin/models}/DiaryEntry.java (95%)
rename app/src/main/java/ch/admin/bag/dp3t/{viewmodel/model => checkin/models}/ReminderOption.java (96%)
rename app/src/main/java/ch/admin/bag/dp3t/{ => checkin}/networking/CrowdNotifierKeyLoadWorker.java (91%)
rename app/src/main/java/ch/admin/bag/dp3t/{ => checkin}/networking/TraceKeysRepository.java (95%)
rename app/src/main/java/ch/admin/bag/dp3t/{networking/models => checkin/networking}/TraceKeysService.java (87%)
rename app/src/main/java/ch/admin/bag/dp3t/{ => checkin}/startup/BootCompletedReceiver.java (84%)
rename app/src/main/java/ch/admin/bag/dp3t/{ => checkin}/storage/DiaryStorage.java (94%)
rename app/src/main/java/ch/admin/bag/dp3t/{util => checkin/utils}/CrowdNotifierReminderHelper.java (95%)
rename app/src/main/java/ch/admin/bag/dp3t/{util => checkin/utils}/NotificationHelper.java (98%)
rename app/src/main/java/ch/admin/bag/dp3t/{util => checkin/utils}/NotificationQuickActionReceiver.java (82%)
rename app/src/main/java/ch/admin/bag/dp3t/{util => checkin/utils}/VenueInfoExtensions.java (95%)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 366e0b4d5..2324fbe37 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -74,11 +74,11 @@
-
+
-
+
-
+
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
similarity index 92%
rename from app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
index 645cf7dfd..ccc6431df 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/CrowdNotifierViewModel.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.viewmodel;
+package ch.admin.bag.dp3t.checkin;
import android.app.Application;
import android.content.BroadcastReceiver;
@@ -20,14 +20,14 @@
import org.crowdnotifier.android.sdk.CrowdNotifier;
import org.crowdnotifier.android.sdk.model.ExposureEvent;
-import ch.admin.bag.dp3t.networking.TraceKeysRepository;
+import ch.admin.bag.dp3t.checkin.networking.TraceKeysRepository;
import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
-import ch.admin.bag.dp3t.viewmodel.model.CrowdNotifierErrorState;
-import ch.admin.bag.dp3t.viewmodel.model.ReminderOption;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
+import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
+import ch.admin.bag.dp3t.checkin.models.ReminderOption;
-import static ch.admin.bag.dp3t.networking.CrowdNotifierKeyLoadWorker.ACTION_NEW_EXPOSURE_NOTIFICATION;
-import static ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper.ACTION_DID_AUTO_CHECKOUT;
+import static ch.admin.bag.dp3t.checkin.networking.CrowdNotifierKeyLoadWorker.ACTION_NEW_EXPOSURE_NOTIFICATION;
+import static ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper.ACTION_DID_AUTO_CHECKOUT;
public class CrowdNotifierViewModel extends AndroidViewModel {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
similarity index 96%
rename from app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
index 7ae8886b9..1aef35468 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CheckInState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.viewmodel.model;
+package ch.admin.bag.dp3t.checkin.models;
import org.crowdnotifier.android.sdk.model.VenueInfo;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
similarity index 97%
rename from app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
index 3d4013f3e..0f01d4cac 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/CrowdNotifierErrorState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.viewmodel.model;
+package ch.admin.bag.dp3t.checkin.models;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
similarity index 95%
rename from app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
index 0b79d4876..b5567bd27 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/model/DiaryEntry.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.storage.model;
+package ch.admin.bag.dp3t.checkin.models;
import org.crowdnotifier.android.sdk.model.VenueInfo;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
similarity index 96%
rename from app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
index 24e1df12b..0acb9c774 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/viewmodel/model/ReminderOption.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.viewmodel.model;
+package ch.admin.bag.dp3t.checkin.models;
import android.content.Context;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
similarity index 91%
rename from app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
index 310975e4b..a5f13f7a1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/CrowdNotifierKeyLoadWorker.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.networking;
+package ch.admin.bag.dp3t.checkin.networking;
import android.content.Context;
import android.content.Intent;
@@ -15,11 +15,11 @@
import org.crowdnotifier.android.sdk.model.ProblematicEventInfo;
import ch.admin.bag.dp3t.BuildConfig;
-import ch.admin.bag.dp3t.storage.DiaryStorage;
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.util.NotificationHelper;
+import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
-import static ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper.autoCheckoutIfNecessary;
+import static ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper.autoCheckoutIfNecessary;
public class CrowdNotifierKeyLoadWorker extends Worker {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
similarity index 95%
rename from app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index 1d00e581e..a318dff64 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.networking;
+package ch.admin.bag.dp3t.checkin.networking;
import android.content.Context;
@@ -11,8 +11,7 @@
import org.dpppt.android.sdk.backend.UserAgentInterceptor;
import ch.admin.bag.dp3t.BuildConfig;
-import ch.admin.bag.dp3t.networking.models.Proto;
-import ch.admin.bag.dp3t.networking.models.TraceKeysService;
+import ch.admin.bag.dp3t.checkin.models.Proto;
import ch.admin.bag.dp3t.storage.SecureStorage;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysService.java
similarity index 87%
rename from app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysService.java
index b7d2f657d..d6fc1db61 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/TraceKeysService.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysService.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.networking.models;
+package ch.admin.bag.dp3t.checkin.networking;
import okhttp3.ResponseBody;
import retrofit2.Call;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/startup/BootCompletedReceiver.java
similarity index 84%
rename from app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/startup/BootCompletedReceiver.java
index 312ea98b0..ff612451d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/startup/BootCompletedReceiver.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/startup/BootCompletedReceiver.java
@@ -1,13 +1,13 @@
-package ch.admin.bag.dp3t.startup;
+package ch.admin.bag.dp3t.checkin.startup;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.util.CrowdNotifierReminderHelper;
-import ch.admin.bag.dp3t.util.NotificationHelper;
-import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper;
+import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
public class BootCompletedReceiver extends BroadcastReceiver {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/storage/DiaryStorage.java
similarity index 94%
rename from app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/storage/DiaryStorage.java
index 823b98e76..78bc28a04 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/DiaryStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/storage/DiaryStorage.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.storage;
+package ch.admin.bag.dp3t.checkin.storage;
import android.content.Context;
import android.content.SharedPreferences;
@@ -15,10 +15,9 @@
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
-
import org.crowdnotifier.android.sdk.model.DayDate;
-import ch.admin.bag.dp3t.storage.model.DiaryEntry;
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
public class DiaryStorage {
@@ -40,8 +39,8 @@ private DiaryStorage(Context context) {
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
- } catch (GeneralSecurityException | IOException ex) {
- ex.printStackTrace();
+ } catch (GeneralSecurityException | IOException e) {
+ throw new RuntimeException(e);
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
similarity index 95%
rename from app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
index edfbc996d..f45ec3915 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/CrowdNotifierReminderHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.util;
+package ch.admin.bag.dp3t.checkin.utils;
import android.app.AlarmManager;
@@ -11,10 +11,11 @@
import org.crowdnotifier.android.sdk.CrowdNotifier;
import ch.admin.bag.dp3t.BuildConfig;
-import ch.admin.bag.dp3t.storage.DiaryStorage;
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.storage.model.DiaryEntry;
-import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
+import ch.admin.bag.dp3t.util.ReminderHelper;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
public class CrowdNotifierReminderHelper extends BroadcastReceiver {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
similarity index 98%
rename from app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
index 418539dce..1ac8da2df 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.util;
+package ch.admin.bag.dp3t.checkin.utils;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -15,6 +15,7 @@
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.MainActivity;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.util.StringUtil;
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_HIGH;
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationQuickActionReceiver.java
similarity index 82%
rename from app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationQuickActionReceiver.java
index 7f4c1d7cd..0dd2285b2 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationQuickActionReceiver.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationQuickActionReceiver.java
@@ -1,10 +1,10 @@
-package ch.admin.bag.dp3t.util;
+package ch.admin.bag.dp3t.checkin.utils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import static ch.admin.bag.dp3t.util.NotificationHelper.ACTION_SNOOZE;
+import static ch.admin.bag.dp3t.checkin.utils.NotificationHelper.ACTION_SNOOZE;
public class NotificationQuickActionReceiver extends BroadcastReceiver {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
similarity index 95%
rename from app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
index 97c8642a0..dba7b0003 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/VenueInfoExtensions.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.util;
+package ch.admin.bag.dp3t.checkin.utils;
import androidx.annotation.DrawableRes;
@@ -8,7 +8,7 @@
import org.crowdnotifier.android.sdk.model.VenueInfo;
import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.networking.models.Proto;
+import ch.admin.bag.dp3t.checkin.models.Proto;
public class VenueInfoExtensions {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
index 62358b6c7..81e7af219 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
@@ -35,7 +35,7 @@
import ch.admin.bag.dp3t.networking.models.TestLocationModel;
import ch.admin.bag.dp3t.networking.models.WhatToDoPositiveTestTextsCollection;
import ch.admin.bag.dp3t.networking.models.WhatToDoPositiveTestTextsModel;
-import ch.admin.bag.dp3t.viewmodel.model.CheckInState;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
public class SecureStorage {
diff --git a/app/src/main/proto/Proto.proto b/app/src/main/proto/Proto.proto
index 6277753ca..cd5a72cda 100644
--- a/app/src/main/proto/Proto.proto
+++ b/app/src/main/proto/Proto.proto
@@ -1,5 +1,5 @@
syntax = "proto3";
-package ch.admin.bag.dp3t.networking.models;
+package ch.admin.bag.dp3t.checkin.models;
message ProblematicEventWrapper {
int32 version = 1;
From 9a191258e5b2b8701e7ed2c93a6ccb6a4d3299fc Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 29 Apr 2021 11:14:29 +0200
Subject: [PATCH 005/207] Fix Liechtenstein-flag not shown on Turkish devices
---
app/src/main/java/ch/admin/bag/dp3t/travel/TravelFragment.java | 3 ++-
app/src/main/java/ch/admin/bag/dp3t/travel/TravelUtils.java | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/travel/TravelFragment.java b/app/src/main/java/ch/admin/bag/dp3t/travel/TravelFragment.java
index d6e5f5fc5..3680a0a9d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/travel/TravelFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/travel/TravelFragment.java
@@ -21,6 +21,7 @@
import androidx.fragment.app.Fragment;
import java.util.List;
+import java.util.Locale;
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.storage.SecureStorage;
@@ -74,7 +75,7 @@ private void setupCountryList() {
ImageView flagImageView = countryItemView.findViewById(R.id.flag_icon);
TextView flagTextView = countryItemView.findViewById(R.id.flag_cc);
- String idName = "flag_" + countryCode.toLowerCase();
+ String idName = "flag_" + countryCode.toLowerCase(Locale.GERMAN);
int drawableRes = UiUtils.getDrawableResourceByName(requireContext(), idName);
if (drawableRes != 0) {
flagImageView.setImageResource(drawableRes);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/travel/TravelUtils.java b/app/src/main/java/ch/admin/bag/dp3t/travel/TravelUtils.java
index c38888e96..dec3e5780 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/travel/TravelUtils.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/travel/TravelUtils.java
@@ -42,7 +42,7 @@ public static void inflateFlagFlow(Flow flowConstraint, List countries)
ImageView flagImageView = flagView.findViewById(R.id.flag_icon);
TextView flagTextView = flagView.findViewById(R.id.flag_cc);
- String idName = "flag_" + country.toLowerCase();
+ String idName = "flag_" + country.toLowerCase(Locale.GERMAN);
int drawableRes = UiUtils.getDrawableResourceByName(context, idName);
if (drawableRes != 0) {
flagImageView.setImageResource(drawableRes);
From fbb2a879fc409acf87c8b1424e56819bb117eaab Mon Sep 17 00:00:00 2001
From: Clea Benz
Date: Thu, 29 Apr 2021 11:30:37 +0200
Subject: [PATCH 006/207] add view binding
---
app/build.gradle | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/build.gradle b/app/build.gradle
index 41ce00c72..9aa2f7ab7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -150,6 +150,10 @@ android {
jvmTarget = "1.8"
}
+ buildFeatures {
+ viewBinding true
+ }
+
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
From 7fb8c7565a2246f6f0e15c5abfc4bf7f6a966d20 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 28 Apr 2021 14:41:50 +0200
Subject: [PATCH 007/207] Implemented Check In Flow
---
app/build.gradle | 28 +-
.../dp3t/checkin/CheckinOverviewFragment.java | 15 +-
.../CameraPermissionExplanationDialog.java | 43 +++
.../checkin/checkinflow/CheckInFragment.java | 105 +++++++
.../checkin/checkinflow/QrCodeAnalyzer.java | 93 ++++++
.../checkinflow/QrCodeScannerFragment.java | 290 ++++++++++++++++++
.../models/CrowdNotifierErrorState.java | 4 +-
.../dp3t/checkin/models/ReminderOption.java | 33 +-
.../bag/dp3t/checkin/utils/ErrorDialog.java | 36 +++
.../bag/dp3t/checkin/utils/ErrorHelper.java | 80 +++++
.../ch/admin/bag/dp3t/home/HomeFragment.java | 5 +-
.../bg_button_default_crowdnotifier.xml | 17 +
.../res/drawable/bg_camera_flash_button.xml | 17 +
.../main/res/drawable/dialog_background.xml | 9 +
app/src/main/res/drawable/ic_flash_off.xml | 10 +
app/src/main/res/drawable/ic_flash_on.xml | 10 +
app/src/main/res/drawable/ic_tea.xml | 10 +
.../res/drawable/qr_scanner_bottom_left.xml | 18 ++
.../res/drawable/qr_scanner_bottom_right.xml | 18 ++
.../main/res/drawable/qr_scanner_top_left.xml | 18 ++
.../res/drawable/qr_scanner_top_right.xml | 18 ++
app/src/main/res/drawable/ripple_rounded.xml | 10 +
.../dialog_camera_permission_explanation.xml | 52 ++++
app/src/main/res/layout/dialog_error.xml | 51 +++
app/src/main/res/layout/fragment_check_in.xml | 142 +++++++++
.../res/layout/fragment_checkin_overview.xml | 2 +-
.../res/layout/fragment_qr_code_scanner.xml | 196 ++++++++++++
app/src/main/res/layout/item_error_status.xml | 2 +-
app/src/main/res/values/colors.xml | 16 +
.../main/res/values/crowdnotifier_styles.xml | 86 ++++++
app/src/main/res/values/dimens.xml | 11 +
app/src/main/res/values/strings.xml | 29 ++
app/src/main/res/values/styles.xml | 1 +
33 files changed, 1457 insertions(+), 18 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CameraPermissionExplanationDialog.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeAnalyzer.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorHelper.java
create mode 100644 app/src/main/res/drawable/bg_button_default_crowdnotifier.xml
create mode 100644 app/src/main/res/drawable/bg_camera_flash_button.xml
create mode 100644 app/src/main/res/drawable/dialog_background.xml
create mode 100644 app/src/main/res/drawable/ic_flash_off.xml
create mode 100644 app/src/main/res/drawable/ic_flash_on.xml
create mode 100644 app/src/main/res/drawable/ic_tea.xml
create mode 100644 app/src/main/res/drawable/qr_scanner_bottom_left.xml
create mode 100644 app/src/main/res/drawable/qr_scanner_bottom_right.xml
create mode 100644 app/src/main/res/drawable/qr_scanner_top_left.xml
create mode 100644 app/src/main/res/drawable/qr_scanner_top_right.xml
create mode 100644 app/src/main/res/drawable/ripple_rounded.xml
create mode 100644 app/src/main/res/layout/dialog_camera_permission_explanation.xml
create mode 100644 app/src/main/res/layout/dialog_error.xml
create mode 100644 app/src/main/res/layout/fragment_check_in.xml
create mode 100644 app/src/main/res/layout/fragment_qr_code_scanner.xml
create mode 100644 app/src/main/res/values/crowdnotifier_styles.xml
diff --git a/app/build.gradle b/app/build.gradle
index d9ca37956..3ec6ae45d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -70,7 +70,9 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-d.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-d.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.dev.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.dev.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.dev'
@@ -82,7 +84,9 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-t.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-t.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-t.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.test.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.test.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.test'
@@ -94,7 +98,9 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-a.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.abnahme.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.abnahme'
@@ -106,7 +112,9 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
}
@@ -117,7 +125,9 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"TODO"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
}
@@ -220,6 +230,14 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+ // QR code scanning
+ def camerax_version = "1.0.0-rc04"
+ implementation "androidx.camera:camera-camera2:${camerax_version}"
+ implementation "androidx.camera:camera-lifecycle:${camerax_version}"
+ implementation "androidx.camera:camera-view:1.0.0-alpha23"
+ //DO NOT UPDATE ZXING TO 3.4.0! (3.4.0 is not compatible with API Level 23 or lower)
+ implementation "com.google.zxing:core:3.3.0"
+
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:core:1.3.0'
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
index c4ed451a0..ef7f4d4ed 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
@@ -4,9 +4,11 @@
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment;
public class CheckinOverviewFragment extends Fragment {
@@ -20,13 +22,24 @@ public static CheckinOverviewFragment newInstance() {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
view.findViewById(R.id.checkin_overview_scan_qr).setOnClickListener(
v -> {
- // TODO
+ showQRCodeScanner();
});
view.findViewById(R.id.checkin_overview_history).setOnClickListener(
v -> {
// TODO
});
+
+ ((Toolbar) view.findViewById(R.id.checkin_overview_toolbar))
+ .setNavigationOnClickListener(v -> getActivity().getSupportFragmentManager().popBackStack());
+ }
+
+ private void showQRCodeScanner() {
+ requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
+ .addToBackStack(QrCodeScannerFragment.class.getCanonicalName())
+ .commit();
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CameraPermissionExplanationDialog.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CameraPermissionExplanationDialog.java
new file mode 100644
index 000000000..87f754696
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CameraPermissionExplanationDialog.java
@@ -0,0 +1,43 @@
+package ch.admin.bag.dp3t.checkin.checkinflow;
+
+import android.content.Context;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import ch.admin.bag.dp3t.R;
+
+public class CameraPermissionExplanationDialog extends AlertDialog {
+
+ private View.OnClickListener grantCameraAccessClickListener;
+
+ public CameraPermissionExplanationDialog(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dialog_camera_permission_explanation);
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ getWindow().setBackgroundDrawableResource(R.drawable.dialog_background);
+
+ TextView grantCameraAccessButton = findViewById(R.id.camera_permission_dialog_ok_button);
+ grantCameraAccessButton.setPaintFlags(grantCameraAccessButton.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+
+ grantCameraAccessButton.setOnClickListener(v -> {
+ dismiss();
+ if (grantCameraAccessClickListener != null) grantCameraAccessClickListener.onClick(v);
+ });
+ findViewById(R.id.camera_permission_dialog_close_button).setOnClickListener(v -> cancel());
+ }
+
+ public void setGrantCameraAccessClickListener(View.OnClickListener listener) {
+ this.grantCameraAccessClickListener = listener;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
new file mode 100644
index 000000000..e3dadf48f
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
@@ -0,0 +1,105 @@
+package ch.admin.bag.dp3t.checkin.checkinflow;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.button.MaterialButtonToggleGroup;
+
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
+import ch.admin.bag.dp3t.checkin.models.ReminderOption;
+import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper;
+import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
+import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensions;
+
+public class CheckInFragment extends Fragment {
+
+ public static final String TAG = CheckInFragment.class.getCanonicalName();
+
+ private CrowdNotifierViewModel viewModel;
+ private VenueInfo venueInfo;
+
+ public CheckInFragment() { super(R.layout.fragment_check_in); }
+
+ public static CheckInFragment newInstance() {
+ return new CheckInFragment();
+ }
+
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ viewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
+ if (viewModel.getCheckInState() != null) {
+ venueInfo = viewModel.getCheckInState().getVenueInfo();
+ }
+ checkIfAutoCheckoutHappened();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ checkIfAutoCheckoutHappened();
+ }
+
+ private void checkIfAutoCheckoutHappened() {
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ TextView titleTextView = view.findViewById(R.id.check_in_fragment_title);
+ TextView subtitleTextView = view.findViewById(R.id.check_in_fragment_subtitle);
+ ImageView venueTypeIcon = view.findViewById(R.id.check_in_fragment_venue_type_icon);
+ View checkInButton = view.findViewById(R.id.check_in_fragment_check_in_button);
+ Toolbar toolbar = view.findViewById(R.id.check_in_fragment_toolbar);
+ MaterialButtonToggleGroup toggleGroup = view.findViewById(R.id.check_in_fragment_toggle_group);
+
+ titleTextView.setText(venueInfo.getTitle());
+ subtitleTextView.setText(VenueInfoExtensions.getSubtitle(venueInfo));
+ venueTypeIcon.setImageResource(VenueInfoExtensions.getVenueTypeDrawable(venueInfo));
+
+ checkInButton.setOnClickListener(v -> {
+ long checkInTime = System.currentTimeMillis();
+ viewModel.startCheckInTimer();
+ viewModel.setCheckedIn(true);
+ viewModel.getCheckInState().setCheckInTime(checkInTime);
+ NotificationHelper.getInstance(getContext()).startOngoingNotification(checkInTime, venueInfo);
+ CrowdNotifierReminderHelper.set8HourReminder(checkInTime, getContext());
+ CrowdNotifierReminderHelper.setAutoCheckOut(checkInTime, getContext());
+ CrowdNotifierReminderHelper
+ .setReminder(checkInTime + viewModel.getSelectedReminderOption().getDelayMillis(), getContext());
+ popBackToHomeFragment();
+ });
+
+ for (ReminderOption option : ReminderOption.values()) {
+ ((MaterialButton) view.findViewById(option.getToggleButtonId())).setText(option.getName(getContext()));
+ }
+ toggleGroup.check(viewModel.getSelectedReminderOption().getToggleButtonId());
+ toggleGroup.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
+ if (isChecked) {
+ ReminderOption selectedReminderOption = ReminderOption.getReminderOptionForToggleButtonId(checkedId);
+ viewModel.setSelectedReminderOption(selectedReminderOption);
+ }
+ });
+
+ toolbar.setNavigationOnClickListener(v -> getActivity().getSupportFragmentManager().popBackStack());
+
+ super.onViewCreated(view, savedInstanceState);
+ }
+
+ private void popBackToHomeFragment() {
+ requireActivity().getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeAnalyzer.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeAnalyzer.java
new file mode 100644
index 000000000..99ff3ead3
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeAnalyzer.java
@@ -0,0 +1,93 @@
+package ch.admin.bag.dp3t.checkin.checkinflow;
+
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.ImageProxy;
+
+import java.nio.ByteBuffer;
+
+import com.google.zxing.*;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.multi.qrcode.QRCodeMultiReader;
+
+public class QrCodeAnalyzer implements ImageAnalysis.Analyzer {
+
+ private final static String TAG = QrCodeAnalyzer.class.getCanonicalName();
+
+ private Listener listener;
+
+ public QrCodeAnalyzer(Listener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void analyze(@NonNull ImageProxy image) {
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int ySize = width * height;
+
+ byte[] imageData = new byte[ySize];
+
+ ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
+
+ int rowStride = image.getPlanes()[0].getRowStride();
+ int pos = 0;
+
+ if (rowStride == width) { // likely
+ yBuffer.get(imageData, 0, ySize);
+ pos += ySize;
+ } else {
+ int yBufferPos = -rowStride; // not an actual position
+ for (; pos < ySize; pos += width) {
+ yBufferPos += rowStride;
+ yBuffer.position(yBufferPos);
+ yBuffer.get(imageData, pos, width);
+ }
+ }
+
+ PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
+ imageData,
+ image.getWidth(), image.getHeight(),
+ 0, 0,
+ image.getWidth(), image.getHeight(),
+ false
+ );
+
+ BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
+
+ try {
+ Result result = new QRCodeMultiReader().decode(binaryBitmap);
+ checkQrCode(result);
+ }
+ // Catch all kinds of dubious exceptions that zxing throws
+ catch (FormatException e) {
+ Log.w(TAG, "Caught FormatException");
+ } catch (ChecksumException e) {
+ Log.w(TAG, "Caught ChecksumException");
+ } catch (NotFoundException e) {
+ // Do nothing
+ listener.noQRCodeFound();
+ } finally {
+ // Must be called else new images won't be received or camera may stall (depending on back pressure setting)
+ image.close();
+ }
+ }
+
+ private void checkQrCode(Result qrCode) {
+ if (qrCode != null) {
+ listener.onQRCodeFound(qrCode.getText());
+ } else {
+ listener.noQRCodeFound();
+ }
+ }
+
+ public interface Listener {
+ void onQRCodeFound(String qrCodeData);
+
+ void noQRCodeFound();
+
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
new file mode 100644
index 000000000..4a3909f63
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -0,0 +1,290 @@
+package ch.admin.bag.dp3t.checkin.checkinflow;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import androidx.annotation.ColorRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.Toolbar;
+import androidx.camera.core.Camera;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.Preview;
+import androidx.camera.core.TorchState;
+import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.view.PreviewView;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+import org.crowdnotifier.android.sdk.utils.QrUtils;
+
+import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
+import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
+import ch.admin.bag.dp3t.checkin.models.ReminderOption;
+import ch.admin.bag.dp3t.checkin.utils.ErrorDialog;
+import ch.admin.bag.dp3t.checkin.utils.ErrorHelper;
+
+public class QrCodeScannerFragment extends Fragment implements QrCodeAnalyzer.Listener {
+
+ public final static String TAG = QrCodeScannerFragment.class.getCanonicalName();
+ private static final int PERMISSION_REQUEST_CAMERA = 13;
+ private static final long MIN_ERROR_VISIBILITY = 1000L;
+
+ private CrowdNotifierViewModel viewModel;
+ private PreviewView previewView;
+ private ListenableFuture cameraProviderFuture;
+ private ExecutorService cameraExecutor;
+ private ImageButton flashButton;
+ private TextView invalidCodeText;
+ private View topLeftIndicator;
+ private View topRightIndicator;
+ private View bottomRightIndicator;
+ private View bottomLeftIndicator;
+ private View errorView;
+ private View mainView;
+ private boolean goToHome = false;
+ private boolean isQRScanningEnabled = true;
+ private long lastUIErrorUpdate = 0L;
+
+ public QrCodeScannerFragment() { super(R.layout.fragment_qr_code_scanner); }
+
+ public static QrCodeScannerFragment newInstance() {
+ return new QrCodeScannerFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ viewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
+ cameraExecutor = Executors.newSingleThreadExecutor();
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onResume() {
+ if (goToHome) {
+ getActivity().getSupportFragmentManager().popBackStack();
+ goToHome = false;
+ } else {
+ isQRScanningEnabled = true;
+ }
+ super.onResume();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (ActivityCompat.checkSelfPermission(requireActivity(), Manifest.permission.CAMERA) !=
+ PackageManager.PERMISSION_GRANTED) {
+ CameraPermissionExplanationDialog dialog = new CameraPermissionExplanationDialog(requireContext());
+ dialog.setOnCancelListener(v -> refreshView(false));
+ dialog.setGrantCameraAccessClickListener(
+ v -> requestPermissions(new String[] { Manifest.permission.CAMERA }, PERMISSION_REQUEST_CAMERA));
+ dialog.show();
+ } else {
+ startCameraAndQrAnalyzer();
+ }
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ previewView = view.findViewById(R.id.camera_preview);
+ flashButton = view.findViewById(R.id.fragment_qr_scanner_flash_button);
+ invalidCodeText = view.findViewById(R.id.qr_code_scanner_invalid_code_text);
+ topLeftIndicator = view.findViewById(R.id.qr_code_scanner_top_left_indicator);
+ topRightIndicator = view.findViewById(R.id.qr_code_scanner_top_right_indicator);
+ bottomLeftIndicator = view.findViewById(R.id.qr_code_scanner_bottom_left_indicator);
+ bottomRightIndicator = view.findViewById(R.id.qr_code_scanner_bottom_right_indicator);
+ errorView = view.findViewById(R.id.fragment_qr_scanner_error_view);
+ mainView = view.findViewById(R.id.fragment_qr_scanner_main_view);
+ Toolbar toolbar = view.findViewById(R.id.fragment_qr_scanner_toolbar);
+
+ toolbar.setNavigationOnClickListener(v -> getActivity().getSupportFragmentManager().popBackStack());
+ }
+
+
+ private void setupFlashButton(Camera camera) {
+
+ if (!camera.getCameraInfo().hasFlashUnit()) {
+ flashButton.setVisibility(View.GONE);
+ } else {
+ flashButton.setVisibility(View.VISIBLE);
+ }
+
+ camera.getCameraInfo().getTorchState().observe(getViewLifecycleOwner(), v -> {
+ if (v == TorchState.ON) {
+ flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_flash_off));
+ } else {
+ flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_flash_on));
+ }
+ });
+
+ flashButton.setOnClickListener(v -> {
+ camera.getCameraControl().enableTorch(camera.getCameraInfo().getTorchState().getValue() == TorchState.OFF);
+ });
+ }
+
+ private void startCameraAndQrAnalyzer() {
+ cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext());
+ cameraProviderFuture.addListener(() -> {
+ try {
+ ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
+ Preview preview = new Preview.Builder().build();
+ preview.setSurfaceProvider(previewView.getSurfaceProvider());
+
+ ImageAnalysis imageAnalyzer = new ImageAnalysis.Builder().build();
+ imageAnalyzer.setAnalyzer(cameraExecutor, new QrCodeAnalyzer(this));
+
+ CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
+
+ cameraProvider.unbindAll();
+ Camera camera = cameraProvider.bindToLifecycle(getViewLifecycleOwner(), cameraSelector, preview, imageAnalyzer);
+ setupFlashButton(camera);
+ } catch (ExecutionException | InterruptedException e) {
+ Log.d(TAG, "Error starting camera " + e.getMessage());
+ e.printStackTrace();
+ }
+ }, ContextCompat.getMainExecutor(requireContext()));
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ cameraExecutor.shutdown();
+ }
+
+ @Override
+ public void noQRCodeFound() {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.NO_CODE_FOUND));
+ }
+
+ @Override
+ public synchronized void onQRCodeFound(String qrCodeData) {
+ if (!isQRScanningEnabled) return;
+ try {
+ VenueInfo venueInfo = CrowdNotifier.getVenueInfo(qrCodeData, BuildConfig.ENTRY_QR_CODE_PREFIX);
+ isQRScanningEnabled = false;
+ if (getActivity() != null) getActivity().runOnUiThread(() -> viewModel.setCheckInState(
+ new CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(),
+ ReminderOption.OFF)));
+ showCheckInFragment();
+ if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.VALID));
+ } catch (QrUtils.QRException e) {
+ handleInvalidQRCodeExceptions(qrCodeData, e);
+ }
+ }
+
+ private void handleInvalidQRCodeExceptions(String qrCodeData, QrUtils.QRException e) {
+ if (e instanceof QrUtils.InvalidQRCodeVersionException) {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> {
+ isQRScanningEnabled = false;
+ ErrorDialog errorDialog = new ErrorDialog(requireContext(), CrowdNotifierErrorState.UPDATE_REQUIRED);
+ errorDialog.setOnDismissListener(v -> isQRScanningEnabled = true);
+ errorDialog.setOnCancelListener(v -> isQRScanningEnabled = true);
+ errorDialog.show();
+ });
+ } else if (e instanceof QrUtils.NotYetValidException) {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.NOT_YET_VALID));
+ } else if (e instanceof QrUtils.NotValidAnymoreException) {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.NOT_VALID_ANYMORE));
+ } else {
+ if (qrCodeData.startsWith(BuildConfig.TRACE_QR_CODE_PREFIX)) {
+ isQRScanningEnabled = false;
+ Intent openBrowserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(qrCodeData));
+ startActivity(openBrowserIntent);
+ goToHome = true;
+ } else {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.INVALID_FORMAT));
+ }
+ }
+ }
+
+ private void indicateInvalidQrCode(QRScannerState qrScannerState) {
+ long currentTime = System.currentTimeMillis();
+ if (lastUIErrorUpdate > currentTime - MIN_ERROR_VISIBILITY && qrScannerState == QRScannerState.NO_CODE_FOUND) {
+ return;
+ }
+ lastUIErrorUpdate = currentTime;
+ int color = R.color.primary;
+ if (qrScannerState == QRScannerState.VALID || qrScannerState == QRScannerState.NO_CODE_FOUND) {
+ invalidCodeText.setVisibility(View.INVISIBLE);
+ } else {
+ invalidCodeText.setVisibility(View.VISIBLE);
+ color = R.color.tertiary;
+ }
+
+ if (qrScannerState == QRScannerState.INVALID_FORMAT) {
+ invalidCodeText.setText(R.string.qrscanner_error);
+ } else if (qrScannerState == QRScannerState.NOT_VALID_ANYMORE) {
+ invalidCodeText.setText(R.string.qr_scanner_error_code_not_valid_anymore);
+ } else if (qrScannerState == QRScannerState.NOT_YET_VALID) {
+ invalidCodeText.setText(R.string.qr_scanner_error_code_not_yet_valid);
+ }
+ setIndicatorColor(topLeftIndicator, color);
+ setIndicatorColor(topRightIndicator, color);
+ setIndicatorColor(bottomLeftIndicator, color);
+ setIndicatorColor(bottomRightIndicator, color);
+ }
+
+ private void setIndicatorColor(View indicator, @ColorRes int color) {
+ LayerDrawable drawable = (LayerDrawable) indicator.getBackground();
+ GradientDrawable stroke = (GradientDrawable) drawable.findDrawableByLayerId(R.id.indicator);
+ if (getContext() == null) return;
+ stroke.setStroke(getResources().getDimensionPixelSize(R.dimen.qr_scanner_indicator_stroke_width),
+ getResources().getColor(color, null));
+ }
+
+ private void showCheckInFragment() {
+ requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, CheckInFragment.newInstance())
+ .addToBackStack(CheckInFragment.class.getCanonicalName())
+ .commitAllowingStateLoss();
+ }
+
+ private void refreshView(boolean cameraPermissionGranted) {
+ if (cameraPermissionGranted) {
+ mainView.setVisibility(View.VISIBLE);
+ errorView.setVisibility(View.GONE);
+ startCameraAndQrAnalyzer();
+ } else {
+ errorView.setVisibility(View.VISIBLE);
+ ErrorHelper.updateErrorView(errorView, CrowdNotifierErrorState.CAMERA_ACCESS_DENIED, null, getContext());
+ mainView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode == PERMISSION_REQUEST_CAMERA) {
+ refreshView(grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+
+ enum QRScannerState {
+ NO_CODE_FOUND, VALID, INVALID_FORMAT, NOT_YET_VALID, NOT_VALID_ANYMORE
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
index 0f01d4cac..b4611505f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
@@ -16,7 +16,9 @@ public enum CrowdNotifierErrorState {
R.drawable.ic_error),
QR_CODE_NOT_VALID_ANYMORE(R.string.error_title, R.string.qr_scanner_error_code_not_valid_anymore, R.string.android_button_ok,
R.drawable.ic_error),
- ALREADY_CHECKED_IN(R.string.error_title, R.string.error_already_checked_in, R.string.android_button_ok, R.drawable.ic_error);
+ ALREADY_CHECKED_IN(R.string.error_title, R.string.error_already_checked_in, R.string.android_button_ok, R.drawable.ic_error),
+ UPDATE_REQUIRED(R.string.error_force_update_title, R.string.error_update_text, R.string.error_action_update,
+ R.drawable.ic_error);
@StringRes private int titleResId;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
index 0acb9c774..d42decc86 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/ReminderOption.java
@@ -1,29 +1,35 @@
package ch.admin.bag.dp3t.checkin.models;
import android.content.Context;
+import androidx.annotation.IdRes;
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.R;
-
public enum ReminderOption {
- OFF(0),
- THIRTY_MINUTES(BuildConfig.FLAVOR.equals("dev") ? 1 : 30),
- ONE_HOUR(60),
- TWO_HOURS(120),
- FOUR_HOURS(240);
+ OFF(0, R.id.check_in_fragment_toggle_button_1),
+ THIRTY_MINUTES(BuildConfig.FLAVOR.equals("dev") ? 1 : 30, R.id.check_in_fragment_toggle_button_2),
+ ONE_HOUR(60, R.id.check_in_fragment_toggle_button_3),
+ TWO_HOURS(120, R.id.check_in_fragment_toggle_button_4),
+ FOUR_HOURS(240, R.id.check_in_fragment_toggle_button_5);
private static final long MINUTE_IN_MILLIS = 60 * 1000L;
private int delayMinutes;
+ @IdRes private int toggleButtonId;
private String name;
- ReminderOption(int delayMinutes) {
+ ReminderOption(int delayMinutes, @IdRes int toggleButtonId) {
this.delayMinutes = delayMinutes;
+ this.toggleButtonId = toggleButtonId;
this.name = name;
}
+ public int getToggleButtonId() {
+ return toggleButtonId;
+ }
+
public int getDelayMinutes() {
return delayMinutes;
}
@@ -46,4 +52,17 @@ public String getName(Context context) {
return context.getString(R.string.reminder_option_hours).replace("{HOURS}", String.valueOf(getDelayHours()));
}
}
+
+ public static ReminderOption getReminderOptionForToggleButtonId(@IdRes int toggleButtonId) {
+ switch (toggleButtonId) {
+ case R.id.check_in_fragment_toggle_button_2:
+ return THIRTY_MINUTES;
+ case R.id.check_in_fragment_toggle_button_3:
+ return ONE_HOUR;
+ case R.id.check_in_fragment_toggle_button_4:
+ return TWO_HOURS;
+ default:
+ return OFF;
+ }
+ }
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
new file mode 100644
index 000000000..1be5a623f
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
@@ -0,0 +1,36 @@
+package ch.admin.bag.dp3t.checkin.utils;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
+
+public class ErrorDialog extends AlertDialog {
+
+ CrowdNotifierErrorState errorState;
+
+ public ErrorDialog(@NonNull Context context, CrowdNotifierErrorState errorState) {
+ super(context);
+ this.errorState = errorState;
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.dialog_error);
+ View closeButton = findViewById(R.id.dialog_error_close_button);
+
+ closeButton.setOnClickListener(v -> dismiss());
+ ErrorHelper.updateErrorView(findViewById(R.id.dialog_error_container), errorState, this::dismiss, getContext());
+
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ getWindow().setBackgroundDrawableResource(R.drawable.dialog_background);
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorHelper.java
new file mode 100644
index 000000000..8fd2df547
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorHelper.java
@@ -0,0 +1,80 @@
+package ch.admin.bag.dp3t.checkin.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.core.content.ContextCompat;
+
+import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
+import ch.admin.bag.dp3t.util.UrlUtil;
+
+public class ErrorHelper {
+
+ public static void updateErrorView(View errorView, CrowdNotifierErrorState errorState, Runnable customButtonClickAction, Context context) {
+ updateErrorView(errorView, errorState, customButtonClickAction, context, true);
+ }
+
+ public static void updateErrorView(View errorView, CrowdNotifierErrorState errorState, Runnable customButtonClickAction, Context context,
+ boolean showButton) {
+ ((TextView) errorView.findViewById(R.id.error_status_title)).setText(errorState.getTitleResId());
+ ((TextView) errorView.findViewById(R.id.error_status_text)).setText(errorState.getTextResId());
+ ((ImageView) errorView.findViewById(R.id.error_status_image))
+ .setImageDrawable(ContextCompat.getDrawable(errorView.getContext(), errorState.getImageResId()));
+
+ TextView buttonView = errorView.findViewById(R.id.error_status_button);
+ if (showButton) {
+ buttonView.setVisibility(View.VISIBLE);
+ buttonView.setText(errorState.getActionResId());
+ buttonView.setPaintFlags(buttonView.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+ buttonView.setOnClickListener(v -> executeErrorAction(errorState, customButtonClickAction, context));
+ } else {
+ buttonView.setVisibility(View.GONE);
+ }
+ }
+
+ private static void executeErrorAction(CrowdNotifierErrorState errorState, Runnable customButtonClickAction, Context context) {
+ if (customButtonClickAction != null) customButtonClickAction.run();
+ switch (errorState) {
+ case NOTIFICATIONS_DISABLED:
+ openNotificationSettings(context);
+ break;
+ case CAMERA_ACCESS_DENIED:
+ openApplicationSettings(context);
+ break;
+ case UPDATE_REQUIRED:
+ updateApp(context);
+ break;
+ }
+ }
+
+ private static void updateApp(Context context) {
+ UrlUtil.openUrl(context, "market://details?id=" + context.getPackageName());
+ }
+
+ private static void openApplicationSettings(Context context) {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", context.getPackageName(), null);
+ intent.setData(uri);
+ context.startActivity(intent);
+ }
+
+ private static void openNotificationSettings(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
+ context.startActivity(intent);
+ } else {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.parse("package:" + context.getPackageName()));
+ context.startActivity(intent);
+ }
+ }
+
+}
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 2a25b5cab..af746f2e1 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
@@ -44,6 +44,7 @@
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.checkin.CheckinOverviewFragment;
+import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment;
import ch.admin.bag.dp3t.contacts.ContactsFragment;
import ch.admin.bag.dp3t.home.model.NotificationState;
import ch.admin.bag.dp3t.home.model.NotificationStateError;
@@ -376,8 +377,8 @@ private void setupCheckinCard() {
checkinCard.findViewById(R.id.checkin_scan).setOnClickListener(
v -> requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, CheckinOverviewFragment.newInstance())
- .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
+ .replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
+ .addToBackStack(QrCodeScannerFragment.class.getCanonicalName())
.commit()
);
}
diff --git a/app/src/main/res/drawable/bg_button_default_crowdnotifier.xml b/app/src/main/res/drawable/bg_button_default_crowdnotifier.xml
new file mode 100644
index 000000000..58e1811b8
--- /dev/null
+++ b/app/src/main/res/drawable/bg_button_default_crowdnotifier.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_camera_flash_button.xml b/app/src/main/res/drawable/bg_camera_flash_button.xml
new file mode 100644
index 000000000..a11ee9229
--- /dev/null
+++ b/app/src/main/res/drawable/bg_camera_flash_button.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/dialog_background.xml b/app/src/main/res/drawable/dialog_background.xml
new file mode 100644
index 000000000..00231c6ac
--- /dev/null
+++ b/app/src/main/res/drawable/dialog_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_flash_off.xml b/app/src/main/res/drawable/ic_flash_off.xml
new file mode 100644
index 000000000..060da882b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_flash_off.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_flash_on.xml b/app/src/main/res/drawable/ic_flash_on.xml
new file mode 100644
index 000000000..1660f9a29
--- /dev/null
+++ b/app/src/main/res/drawable/ic_flash_on.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_tea.xml b/app/src/main/res/drawable/ic_tea.xml
new file mode 100644
index 000000000..df643ae3e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tea.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/qr_scanner_bottom_left.xml b/app/src/main/res/drawable/qr_scanner_bottom_left.xml
new file mode 100644
index 000000000..26e8ab204
--- /dev/null
+++ b/app/src/main/res/drawable/qr_scanner_bottom_left.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/qr_scanner_bottom_right.xml b/app/src/main/res/drawable/qr_scanner_bottom_right.xml
new file mode 100644
index 000000000..628e53eaa
--- /dev/null
+++ b/app/src/main/res/drawable/qr_scanner_bottom_right.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/qr_scanner_top_left.xml b/app/src/main/res/drawable/qr_scanner_top_left.xml
new file mode 100644
index 000000000..710fb8a69
--- /dev/null
+++ b/app/src/main/res/drawable/qr_scanner_top_left.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/qr_scanner_top_right.xml b/app/src/main/res/drawable/qr_scanner_top_right.xml
new file mode 100644
index 000000000..e07149dcc
--- /dev/null
+++ b/app/src/main/res/drawable/qr_scanner_top_right.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ripple_rounded.xml b/app/src/main/res/drawable/ripple_rounded.xml
new file mode 100644
index 000000000..7c2cbd288
--- /dev/null
+++ b/app/src/main/res/drawable/ripple_rounded.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_camera_permission_explanation.xml b/app/src/main/res/layout/dialog_camera_permission_explanation.xml
new file mode 100644
index 000000000..6a85f4136
--- /dev/null
+++ b/app/src/main/res/layout/dialog_camera_permission_explanation.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml
new file mode 100644
index 000000000..047853805
--- /dev/null
+++ b/app/src/main/res/layout/dialog_error.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_check_in.xml b/app/src/main/res/layout/fragment_check_in.xml
new file mode 100644
index 000000000..be53aa6ad
--- /dev/null
+++ b/app/src/main/res/layout/fragment_check_in.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
index dd381d3b6..25f552b23 100644
--- a/app/src/main/res/layout/fragment_checkin_overview.xml
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -16,7 +16,7 @@
android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_error_status.xml b/app/src/main/res/layout/item_error_status.xml
index b09550816..188177d80 100644
--- a/app/src/main/res/layout/item_error_status.xml
+++ b/app/src/main/res/layout/item_error_status.xml
@@ -14,7 +14,7 @@
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_margin="@dimen/marging_status_error"
- android:background="@color/grey_light"
+ android:background="@color/light_grey"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="@dimen/spacing_large">
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index add82595d..f4c8b4795 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -71,4 +71,20 @@
#754A4969#0000@color/grey_medium
+
+
+
+
+ #000000
+ #413f8d
+ #dddcea
+ #292859
+ #00a690
+ #f34e70
+
+ #f2f2f2
+ #c0f2f2f2
+
+
+
diff --git a/app/src/main/res/values/crowdnotifier_styles.xml b/app/src/main/res/values/crowdnotifier_styles.xml
new file mode 100644
index 000000000..67fd24f11
--- /dev/null
+++ b/app/src/main/res/values/crowdnotifier_styles.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index e0192b51d..bd9c85962 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -96,4 +96,15 @@
26dp20dp
+
+
+
+
+ 36dp
+ 50dp
+ 5dp
+ -6dp
+
+ 72dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7b876070f..1bb28b1bf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1503,4 +1503,33 @@ If so, contact the Coronavirus Infoline:""Snooze"
+ "Update required"
+
+ "This QR code is not compatible with the current version of the app. Please update the app to scan it the code."
+
+
+ "Update now"
+
+
+
+ "NotifyMe requires access to your camera. The camera will only be used for scanning QR codes."
+
+
+ "Grant camera access"
+
+
+ "Check-in"
+
+
+ "Scan QR code to check in"
+
+
+ "Checking in"
+
+
+ "Set reminder"
+
+
+ "Check In Now"
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 949c0d49c..0e283530a 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -329,4 +329,5 @@ a
@font/inter_light
+
From be6c70103c2d215f629ad92fdfe1de97df3067af Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 29 Apr 2021 09:34:16 +0200
Subject: [PATCH 008/207] Implement Checked-In State on Homescreen
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 63 +++++--
.../ch/admin/bag/dp3t/util/StringUtil.java | 40 ++++-
app/src/main/res/drawable/ic_qr.xml | 10 ++
app/src/main/res/layout/card_checkin.xml | 77 ++++++++-
app/src/main/res/values-bs/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-de/strings.xml | 155 ++++++++++++++++++
app/src/main/res/values-es/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-fr/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-hr/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-it/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-pt/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-rm/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-sq/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-sr/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-ti/strings.xml | 153 +++++++++++++++++
app/src/main/res/values-tr/strings.xml | 153 +++++++++++++++++
app/src/main/res/values/strings.xml | 3 +
app/src/main/res/values/styles.xml | 8 +-
18 files changed, 2011 insertions(+), 28 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_qr.xml
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 af746f2e1..0a0ef0796 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
@@ -44,6 +44,7 @@
import ch.admin.bag.dp3t.BuildConfig;
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.QrCodeScannerFragment;
import ch.admin.bag.dp3t.contacts.ContactsFragment;
import ch.admin.bag.dp3t.home.model.NotificationState;
@@ -69,6 +70,7 @@ public class HomeFragment extends Fragment {
private static final String TAG = "HomeFragment";
private TracingViewModel tracingViewModel;
+ private CrowdNotifierViewModel crowdNotifierViewModel;
private HeaderView headerView;
private ScrollView scrollView;
@@ -103,6 +105,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
secureStorage = SecureStorage.getInstance(getContext());
tracingViewModel = new ViewModelProvider(requireActivity()).get(TracingViewModel.class);
+ crowdNotifierViewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
getChildFragmentManager()
.beginTransaction()
@@ -366,21 +369,53 @@ private boolean isNotificationChannelEnabled(Context context, @Nullable String c
}
private void setupCheckinCard() {
- checkinCard.setOnClickListener(
- v -> requireActivity().getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, CheckinOverviewFragment.newInstance())
- .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
- .commit()
- );
- checkinCard.findViewById(R.id.checkin_scan).setOnClickListener(
- v -> requireActivity().getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
- .addToBackStack(QrCodeScannerFragment.class.getCanonicalName())
- .commit()
- );
+ checkinCard.setOnClickListener(v -> showCheckinOverviewFragment());
+
+ View checkinView = checkinCard.findViewById(R.id.checkin_view);
+ View checkoutView = checkinCard.findViewById(R.id.checkout_view);
+
+ crowdNotifierViewModel.isCheckedIn().observe(getViewLifecycleOwner(), isCheckedIn -> {
+ if (isCheckedIn) {
+ checkoutView.setVisibility(View.VISIBLE);
+ checkinView.setVisibility(View.GONE);
+ crowdNotifierViewModel.startCheckInTimer();
+ } else {
+ checkoutView.setVisibility(View.GONE);
+ checkinView.setVisibility(View.VISIBLE);
+ }
+ });
+
+ checkinCard.findViewById(R.id.checkin_button).setOnClickListener(v -> showQrCodeScannerFragment());
+ checkinCard.findViewById(R.id.checkout_button).setOnClickListener(v -> showCheckOutFragment());
+
+ TextView checkinTime = checkinCard.findViewById(R.id.checkin_time);
+ crowdNotifierViewModel.getTimeSinceCheckIn().observe(getViewLifecycleOwner(),
+ duration -> checkinTime.setText(StringUtil.getShortDurationString(duration)));
+ }
+
+ private void showCheckOutFragment() {
+ //TODO
+ }
+
+ private void showCheckedInFragment() {
+ //TODO
+ }
+
+ private void showQrCodeScannerFragment() {
+ requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
+ .addToBackStack(QrCodeScannerFragment.class.getCanonicalName())
+ .commit();
+ }
+
+ private void showCheckinOverviewFragment() {
+ requireActivity().getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, CheckinOverviewFragment.newInstance())
+ .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
+ .commit();
}
private void setupTravelCard() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
index 3a835771e..a47b0eac1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
@@ -7,7 +7,6 @@
*
* SPDX-License-Identifier: MPL-2.0
*/
-
package ch.admin.bag.dp3t.util;
import android.graphics.Typeface;
@@ -19,13 +18,16 @@
import java.math.BigInteger;
import java.util.Calendar;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
public class StringUtil {
+ private static final long ONE_HOUR = TimeUnit.HOURS.toMillis(1);
+
/**
* Creates a spannable where the {@code boldString} is set to bold within the {@code fullString}.
* Be aware that this only applies to the first occurence.
- *
* @param fullString The entire string
* @param boldString The partial string to be made bold
* @return A partially bold spannable
@@ -48,7 +50,7 @@ public static String toHex(byte[] array) {
BigInteger bi = new BigInteger(1, array);
String hex = bi.toString(16);
int paddingLength = (array.length * 2) - hex.length();
- if(paddingLength > 0)
+ if (paddingLength > 0)
return String.format("%0" + paddingLength + "d", 0) + hex;
else
return hex;
@@ -68,4 +70,36 @@ private static String prependZero(int timeUnit) {
}
}
+ /**
+ * Formats a duration in milliseconds to a String of hours, minutes and seconds, or to only hours and minutes if the
+ * duration is more than 10 hours
+ * @param duration in milliseconds
+ * @return a formatted duration String
+ */
+ public static String getShortDurationString(long duration) {
+ if (duration >= TimeUnit.HOURS.toMillis(10)) {
+ return String.format(Locale.GERMAN, "%d:%02d",
+ TimeUnit.MILLISECONDS.toHours(duration),
+ TimeUnit.MILLISECONDS.toMinutes(duration - TimeUnit.HOURS.toMillis(TimeUnit.MILLISECONDS.toHours(duration)))
+ );
+ } else {
+ return getDurationString(duration);
+ }
+ }
+
+ public static String getDurationString(long duration) {
+ if (duration >= ONE_HOUR) {
+ return String.format(Locale.GERMAN, "%d:%02d:%02d",
+ TimeUnit.MILLISECONDS.toHours(duration),
+ TimeUnit.MILLISECONDS.toMinutes(duration - TimeUnit.HOURS.toMillis(TimeUnit.MILLISECONDS.toHours(duration))),
+ TimeUnit.MILLISECONDS.toSeconds(duration - TimeUnit.MINUTES.toMillis(TimeUnit.MILLISECONDS.toMinutes(duration)))
+ );
+ } else {
+ return String.format(Locale.GERMAN, "%02d:%02d",
+ TimeUnit.MILLISECONDS.toMinutes(duration),
+ TimeUnit.MILLISECONDS.toSeconds(duration - TimeUnit.MINUTES.toMillis(TimeUnit.MILLISECONDS.toMinutes(duration)))
+ );
+ }
+ }
+
}
diff --git a/app/src/main/res/drawable/ic_qr.xml b/app/src/main/res/drawable/ic_qr.xml
new file mode 100644
index 000000000..e5dc53b1c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_qr.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/card_checkin.xml b/app/src/main/res/layout/card_checkin.xml
index 3c568fcf2..47337fd07 100644
--- a/app/src/main/res/layout/card_checkin.xml
+++ b/app/src/main/res/layout/card_checkin.xml
@@ -1,5 +1,6 @@
+ android:orientation="horizontal"
+ android:paddingBottom="5dp">
+ android:text="[TBT] My Check-Ins" />
+ app:tint="@color/dark_main" />
-
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/spacing_small"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index b18d230bb..2a81b1727 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -1413,4 +1413,157 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d54060630..681bd8214 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1473,4 +1473,159 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"In 1 Tag"
+
+
+ "Check-out Erinnerung"
+
+
+ "Möchten Sie auschecken?"
+
+
+ "Automatischer Checkout"
+
+
+ "Sie wurden automatisch ausgecheckt."
+
+
+ "QR-Code scannen und einchecken"
+
+
+ "Kein gültiger QR-Code!"
+
+
+ "Von"
+
+
+ "Bis"
+
+
+ "Notiz hinzufügen"
+
+
+ "zb. Mit wem waren Sie da?"
+
+
+ "Fertig"
+
+
+ "Check-out Erinnerung"
+
+
+ "Aus"
+
+
+ "Authentifizieren Sie sich für den Zugriff auf das Tagebuch"
+
+
+ "{HOURS} h"
+
+
+ "{MINUTES}'"
+
+
+ "Die App benötigt Zugriff auf die Kamera, um den QR-Code scannen zu können."
+
+
+ "Tagebuch"
+
+
+ "Check-in"
+
+
+ "Die Kamera wird benötigt, um den QR-Code zu scannen."
+
+
+ "Erlauben Sie Face-ID, um sicher auf Ihr Tagebuch zuzugreifen."
+
+
+ "Authentifizieren Sie sich für den Zugriff auf das Tagebuch"
+
+
+ "Check-out"
+
+
+ "Check-Ins"
+
+
+ "Check-In"
+
+
+ "QR-Code erstellen"
+
+
+ "Veranstaltungen"
+
+
+ "Jetzt einchecken"
+
+
+ "Die Erinnerung konnte nicht gesetzt werden"
+
+
+ "Damit Sie Check-out Erinnerungen erhalten, müssen Sie \\"Mitteilungen\\" in den App-Einstellungen erlauben."
+
+
+ "Anpassen"
+
+
+ "Bearbeiten"
+
+
+ "Noch keine Tagebucheinträge vorhanden"
+
+
+ "Die Veranstaltungen, die besucht wurden, erscheinen im Tagebuch nach dem Check-out."
+
+
+ "SwissCovid benötigt Zugriff auf die Kamera Ihres Gerätes. Die Kamera wird nur verwendet, um QR Codes zu scannen."
+
+
+ "Kamera-Zugriff erlauben"
+
+
+
+ "z.B. Raum- oder Eventname"
+
+
+
+ "Kategorie"
+
+
+ "Andere"
+
+
+ "Sitzungsraum"
+
+
+ "Kafeteria"
+
+
+ "Privater Event"
+
+
+ "Titel"
+
+
+ "Druck-PDF anzeigen"
+
+
+ "Kantine"
+
+
+ "Bibliothek"
+
+
+ "Vorlesungssaal"
+
+
+ "Laden"
+
+
+ "Fitnesscenter"
+
+
+ "Küchenbereich"
+
+
+ "Büroräume"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index d0e36d6e7..5bfabafa9 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1404,4 +1404,157 @@ Contacte con la Infoline del coronavirus:"
"En 1 día"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index f2189baa3..83bb2218b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1439,4 +1439,157 @@ Contactez l'infoline coronavirus :"
"Dans 1 jour"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index ea609639f..669e72272 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1409,4 +1409,157 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 370af1dac..78c1ef1a8 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1447,4 +1447,157 @@ Contatta la Infoline coronavirus:"
"Fra 1 giorno"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index c353b1946..0285d87a5 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1414,4 +1414,157 @@ Contacte a linha de informação coronavírus:"
"Em 1 dia"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 45ccf05c3..5b2ce53d2 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1409,4 +1409,157 @@ Alura contactai la Infoline coronavirus:"
"En 1 di"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 3c0b15669..aaaa931b0 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1414,4 +1414,157 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"Në 1 ditë"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 60b20ea8a..d94d37ae4 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1409,4 +1409,157 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index 647461c17..30ab827d7 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1389,4 +1389,157 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ኣብ 1 መዓልቲ"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 3868750d6..4d6c22508 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1391,4 +1391,157 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"1 gün sonra"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1bb28b1bf..663d2f27c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1532,4 +1532,7 @@ If so, contact the Coronavirus Infoline:"
"Check In Now"
+
+ "Check-out"
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 0e283530a..0ef106e8e 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -63,7 +63,8 @@
-a
+ a
+
@@ -159,6 +160,10 @@ a
@color/white
+
+
-
From 612d2bb97694a7819918f66a8278ec6030a0ac50 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 29 Apr 2021 15:15:33 +0200
Subject: [PATCH 009/207] Implement Checked-In State on CheckinOverviewFragment
---
.../dp3t/checkin/CheckinOverviewFragment.java | 43 +++-
.../checkinflow/QrCodeScannerFragment.java | 7 +-
.../models/CrowdNotifierErrorState.java | 2 +-
.../checkin/utils/NotificationHelper.java | 4 +-
.../ch/admin/bag/dp3t/home/HomeFragment.java | 4 -
app/src/main/res/layout/fragment_check_in.xml | 2 +-
.../res/layout/fragment_checkin_overview.xml | 88 +++++++-
app/src/main/res/values-bs/strings.xml | 21 ++
app/src/main/res/values-de/strings.xml | 21 ++
app/src/main/res/values-es/strings.xml | 21 ++
app/src/main/res/values-fr/strings.xml | 21 ++
app/src/main/res/values-hr/strings.xml | 21 ++
app/src/main/res/values-it/strings.xml | 21 ++
app/src/main/res/values-pt/strings.xml | 21 ++
app/src/main/res/values-rm/strings.xml | 21 ++
app/src/main/res/values-sq/strings.xml | 21 ++
app/src/main/res/values-sr/strings.xml | 21 ++
app/src/main/res/values-ti/strings.xml | 21 ++
app/src/main/res/values-tr/strings.xml | 21 ++
app/src/main/res/values/strings.xml | 213 ++++++++++++------
app/src/main/res/values/styles.xml | 1 -
21 files changed, 523 insertions(+), 93 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
index ef7f4d4ed..b211636e4 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/CheckinOverviewFragment.java
@@ -2,28 +2,35 @@
import android.os.Bundle;
import android.view.View;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment;
+import ch.admin.bag.dp3t.util.StringUtil;
public class CheckinOverviewFragment extends Fragment {
+ private CrowdNotifierViewModel crowdNotifierViewModel;
+
public static CheckinOverviewFragment newInstance() {
return new CheckinOverviewFragment();
}
public CheckinOverviewFragment() { super(R.layout.fragment_checkin_overview); }
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ crowdNotifierViewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
+ }
+
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- view.findViewById(R.id.checkin_overview_scan_qr).setOnClickListener(
- v -> {
- showQRCodeScanner();
- });
view.findViewById(R.id.checkin_overview_history).setOnClickListener(
v -> {
@@ -32,9 +39,35 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
((Toolbar) view.findViewById(R.id.checkin_overview_toolbar))
.setNavigationOnClickListener(v -> getActivity().getSupportFragmentManager().popBackStack());
+
+ View checkinCard = view.findViewById(R.id.checkin_overview_scan_qr);
+ View checkinView = checkinCard.findViewById(R.id.checkin_view);
+ View checkoutView = checkinCard.findViewById(R.id.checkout_view);
+
+ crowdNotifierViewModel.isCheckedIn().observe(getViewLifecycleOwner(), isCheckedIn -> {
+ if (isCheckedIn) {
+ checkoutView.setVisibility(View.VISIBLE);
+ checkinView.setVisibility(View.GONE);
+ crowdNotifierViewModel.startCheckInTimer();
+ } else {
+ checkoutView.setVisibility(View.GONE);
+ checkinView.setVisibility(View.VISIBLE);
+ }
+ });
+
+ checkinCard.findViewById(R.id.checkin_button).setOnClickListener(v -> showQrCodeScannerFragment());
+ checkinCard.findViewById(R.id.checkout_button).setOnClickListener(v -> showCheckOutFragment());
+
+ TextView checkinTime = checkinCard.findViewById(R.id.checkin_time);
+ crowdNotifierViewModel.getTimeSinceCheckIn().observe(getViewLifecycleOwner(),
+ duration -> checkinTime.setText(StringUtil.getShortDurationString(duration)));
+ }
+
+ private void showCheckOutFragment() {
+ //TODO
}
- private void showQRCodeScanner() {
+ private void showQrCodeScannerFragment() {
requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
index 4a3909f63..029b8890f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -161,9 +161,12 @@ private void startCameraAndQrAnalyzer() {
cameraProvider.unbindAll();
Camera camera = cameraProvider.bindToLifecycle(getViewLifecycleOwner(), cameraSelector, preview, imageAnalyzer);
setupFlashButton(camera);
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.d(TAG, "Error starting camera " + e.getMessage());
- e.printStackTrace();
+ throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ Log.w("QR Analysis Interrupted", e.getMessage());
+ Thread.currentThread().interrupt();
}
}, ContextCompat.getMainExecutor(requireContext()));
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
index b4611505f..d5913b1ca 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CrowdNotifierErrorState.java
@@ -17,7 +17,7 @@ public enum CrowdNotifierErrorState {
QR_CODE_NOT_VALID_ANYMORE(R.string.error_title, R.string.qr_scanner_error_code_not_valid_anymore, R.string.android_button_ok,
R.drawable.ic_error),
ALREADY_CHECKED_IN(R.string.error_title, R.string.error_already_checked_in, R.string.android_button_ok, R.drawable.ic_error),
- UPDATE_REQUIRED(R.string.error_force_update_title, R.string.error_update_text, R.string.error_action_update,
+ UPDATE_REQUIRED(R.string.error_update_title, R.string.error_update_text, R.string.error_action_update,
R.drawable.ic_error);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
index 1ac8da2df..28beb363b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
@@ -115,8 +115,8 @@ public void showExposureNotification(long exposureId) {
Notification notification = getNotificationBuilder(CHANNEL_ID_EXPOSURE_NOTIFICATION)
.setContentIntent(createExposurePendingIntent(exposureId))
- .setContentTitle(context.getString(R.string.exposure_notification_title))
- .setContentText(context.getString(R.string.exposure_notification_body))
+ .setContentTitle(context.getString(R.string.push_exposed_title))
+ .setContentText(context.getString(R.string.push_exposed_text))
.build();
notificationManager.notify((int) exposureId, notification);
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 0a0ef0796..6711fd032 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
@@ -398,10 +398,6 @@ private void showCheckOutFragment() {
//TODO
}
- private void showCheckedInFragment() {
- //TODO
- }
-
private void showQrCodeScannerFragment() {
requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
diff --git a/app/src/main/res/layout/fragment_check_in.xml b/app/src/main/res/layout/fragment_check_in.xml
index be53aa6ad..677f684dc 100644
--- a/app/src/main/res/layout/fragment_check_in.xml
+++ b/app/src/main/res/layout/fragment_check_in.xml
@@ -13,7 +13,7 @@
android:layout_height="wrap_content"
app:navigationContentDescription="@string/android_accessibility_back_button"
app:navigationIcon="@drawable/ic_arrow_back"
- app:title="@string/check_in_screen_title" />
+ app:title="@string/checkin_title" />
-
+ android:layout_marginHorizontal="@dimen/spacing_small"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 681bd8214..46c2af979 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1628,4 +1628,25 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Büroräume"
+ "Info"
+ "Netzwerk Fehler"
+ "Überprüfen Sie Ihre Internet Verbindung."
+ "Erneut versuchen"
+ "Mitteilungen deaktiviert"
+ "Ändern Sie die Einstellungen, um bei einer Meldung direkt informiert zu werden."
+ "Einstellungen ändern"
+ "Kein Zugriff auf Kamera"
+ "Die App benötigt Zugriff auf die Kamera, um den QR-Code scannen zu können."
+ "Fehler"
+ "QR-Code noch nicht gültig"
+ "QR-Code nicht mehr gültig"
+ "Sie sind bereits eingecheckt."
+ "Dieser QR Code ist nicht kompatibel mit der installierten App Version. Führen Sie ein Update der App durch, um den QR Code zu scannen."
+ "Update benötigt"
+ "Jetzt updaten"
+ "CrowdNotifier Meldungen"
+ "Check out"
+ "Snooze"
+ "Permanente Checkin Meldung"
+ "Sie sind eingecheckt seit {TIME} Uhr."
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 5bfabafa9..30a2397cf 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1557,4 +1557,25 @@ Contacte con la Infoline del coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 83bb2218b..0ab861dd7 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1592,4 +1592,25 @@ Contactez l'infoline coronavirus :"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 669e72272..c415a9466 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1562,4 +1562,25 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 78c1ef1a8..7f49a5039 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1600,4 +1600,25 @@ Contatta la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 0285d87a5..1fb7fd495 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1567,4 +1567,25 @@ Contacte a linha de informação coronavírus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 5b2ce53d2..ad2190ecf 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1562,4 +1562,25 @@ Alura contactai la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index aaaa931b0..7085faf72 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1567,4 +1567,25 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index d94d37ae4..dc8616037 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1562,4 +1562,25 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index 30ab827d7..df2175989 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1542,4 +1542,25 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 4d6c22508..b196d97c7 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1544,4 +1544,25 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 663d2f27c..569940d85 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1437,102 +1437,177 @@ If so, contact the Coronavirus Infoline:"
"In 1 day"
-
- "off"
+
+
-
- "{HOURS} h"
+
+
-
- "{MINUTES}'"
+
+
- "Connection error"
- "Check your internet connection"
- "Deactivate notifications"
- "Edit the app settings to receive report-notifications."
- "Background updates are off"
- "Edit the app settings to be informed at all times."
- "No camera access"
- "The app needs camera access to scan the QR code."
- "Edit settings"
- "Try again"
+
+
-
- "Error"
+
+
-
- "You are already checked in."
+
+
- "Code is not valid yet"
- "Code is not valid anymore"
+
+
-
-
- "No valid code!"
+
+
-
- "Automated checkout"
+
+
-
- "You were checked out automatically."
+
+
-
- "New message"
+
+
-
- "Open the app to read the message"
+
+
-
- "Reports"
+
+
-
- "Check-Out Reminder"
+
+
-
- "Do you want to check out?"
+
+
-
- "Ongoing Check-in Notification"
+
+
-
- "You are checked in since {TIME}."
+
+
-
- "Check out"
+
+
-
- "Snooze"
+
+
- "Update required"
-
- "This QR code is not compatible with the current version of the app. Please update the app to scan it the code."
+
+
-
- "Update now"
+
+
-
-
- "NotifyMe requires access to your camera. The camera will only be used for scanning QR codes."
+
+
-
- "Grant camera access"
+
+
-
- "Check-in"
+
+
-
- "Scan QR code to check in"
+
+
-
- "Checking in"
+
+
-
- "Set reminder"
+
+
-
- "Check In Now"
+
+
-
- "Check-out"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 0ef106e8e..478fc104c 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -63,7 +63,6 @@
- a
+
+
@@ -83,4 +88,12 @@
@dimen/spacing_huge@dimen/spacing_huge
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index bd9c85962..6e4427309 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -106,5 +106,8 @@
-6dp72dp
+ 11dp
+ 2dp
+ 3dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 569940d85..304d08d62 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1610,4 +1610,26 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 231364bf3187cc5a95e5e1d155ec9769a5b0a395 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 29 Apr 2021 20:01:46 +0200
Subject: [PATCH 012/207] Implement Notification Intent Handling
---
.../java/ch/admin/bag/dp3t/MainActivity.java | 171 ++++++++++++++----
.../dp3t/checkin/CrowdNotifierViewModel.java | 1 +
.../checkin/checkinflow/CheckOutFragment.java | 2 +-
.../utils/CrowdNotifierReminderHelper.java | 5 +-
.../checkin/utils/NotificationHelper.java | 16 +-
app/src/main/res/anim/modal_pop_enter.xml | 11 ++
app/src/main/res/anim/modal_pop_exit.xml | 10 +
app/src/main/res/anim/modal_slide_enter.xml | 8 +
app/src/main/res/anim/modal_slide_exit.xml | 17 ++
9 files changed, 192 insertions(+), 49 deletions(-)
create mode 100644 app/src/main/res/anim/modal_pop_enter.xml
create mode 100644 app/src/main/res/anim/modal_pop_exit.xml
create mode 100644 app/src/main/res/anim/modal_slide_enter.xml
create mode 100644 app/src/main/res/anim/modal_slide_exit.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
index 8ef66e42a..9046f14bf 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
@@ -9,8 +9,11 @@
*/
package ch.admin.bag.dp3t;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
@@ -20,9 +23,22 @@
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+import org.crowdnotifier.android.sdk.model.VenueInfo;
+import org.crowdnotifier.android.sdk.utils.QrUtils;
import org.dpppt.android.sdk.DP3T;
+import ch.admin.bag.dp3t.checkin.CheckinOverviewFragment;
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
+import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment;
+import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment;
+import ch.admin.bag.dp3t.checkin.models.CheckInState;
+import ch.admin.bag.dp3t.checkin.models.CrowdNotifierErrorState;
+import ch.admin.bag.dp3t.checkin.models.ReminderOption;
+import ch.admin.bag.dp3t.checkin.networking.CrowdNotifierKeyLoadWorker;
+import ch.admin.bag.dp3t.checkin.utils.ErrorDialog;
import ch.admin.bag.dp3t.home.model.TracingStatusInterface;
import ch.admin.bag.dp3t.inform.InformActivity;
import ch.admin.bag.dp3t.networking.ConfigWorker;
@@ -34,6 +50,12 @@
import ch.admin.bag.dp3t.viewmodel.TracingViewModel;
import ch.admin.bag.dp3t.whattodo.WtdPositiveTestFragment;
+import static ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper.ACTION_DID_AUTO_CHECKOUT;
+import static ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper.autoCheckoutIfNecessary;
+import static ch.admin.bag.dp3t.checkin.utils.NotificationHelper.ACTION_CHECK_OUT_NOW;
+import static ch.admin.bag.dp3t.checkin.utils.NotificationHelper.ACTION_CROWDNOTIFIER_REMINDER_NOTIFICATION;
+import static ch.admin.bag.dp3t.checkin.utils.NotificationHelper.ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION;
+import static ch.admin.bag.dp3t.checkin.utils.NotificationHelper.ACTION_ONGOING_NOTIFICATION;
import static ch.admin.bag.dp3t.inform.InformActivity.EXTRA_COVIDCODE;
import static ch.admin.bag.dp3t.updateboarding.UpdateBoardingActivity.UPDATE_BOARDING_VERSION;
import static ch.admin.bag.dp3t.util.NotificationUtil.ACTION_ACTIVATE_TRACING;
@@ -43,16 +65,16 @@ public class MainActivity extends FragmentActivity {
public static final String ACTION_EXPOSED_GOTO_REPORTS = "ACTION_EXPOSED_GOTO_REPORTS";
public static final String ACTION_INFORMED_GOTO_REPORTS = "ACTION_INFORMED_GOTO_REPORTS";
- private static final String STATE_CONSUMED_EXPOSED_INTENT = "STATE_CONSUMED_EXPOSED_INTENT";
- private static final String STATE_CONSUMED_COVIDCODE_INTENT = "STATE_CONSUMED_COVIDCODE_INTENT";
- private boolean consumedExposedIntent;
- private boolean consumedCovidcodeIntent;
+ private static final String KEY_IS_INTENT_CONSUMED = "KEY_IS_INTENT_CONSUMED";
+ private boolean isIntentConsumed = false;
private SecureStorage secureStorage;
private TracingViewModel tracingViewModel;
+ private CrowdNotifierViewModel crowdNotifierViewModel;
private AlertDialog forceUpdateDialog;
+
private final ActivityResultLauncher onAndUpdateBoardingLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
if (activityResult.getResultCode() == RESULT_OK) {
@@ -64,6 +86,14 @@ public class MainActivity extends FragmentActivity {
}
});
+ private BroadcastReceiver autoCheckoutBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ showHomeFragment();
+ }
+ };
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -94,6 +124,8 @@ protected void onCreate(Bundle savedInstanceState) {
});
ConfigWorker.scheduleConfigWorkerIfOutdated(this);
+ CrowdNotifierKeyLoadWorker.startKeyLoadWorker(this);
+ CrowdNotifierKeyLoadWorker.cleanUpOldData(this);
if (savedInstanceState == null) {
boolean onboardingCompleted = secureStorage.getOnboardingCompleted();
@@ -106,81 +138,116 @@ protected void onCreate(Bundle savedInstanceState) {
showHomeFragment();
}
} else {
- consumedExposedIntent = savedInstanceState.getBoolean(STATE_CONSUMED_EXPOSED_INTENT);
- consumedCovidcodeIntent = savedInstanceState.getBoolean(STATE_CONSUMED_COVIDCODE_INTENT);
+ isIntentConsumed = savedInstanceState.getBoolean(KEY_IS_INTENT_CONSUMED);
}
tracingViewModel = new ViewModelProvider(this).get(TracingViewModel.class);
+ crowdNotifierViewModel = new ViewModelProvider(this).get(CrowdNotifierViewModel.class);
tracingViewModel.sync();
- checkRedirectionIntents();
- }
-
- public void checkRedirectionIntents() {
- checkIntentForActions();
-
- if (!consumedExposedIntent) {
- boolean isOpenLeitfadenPending = secureStorage.isOpenLeitfadenPending();
- boolean isExposed = tracingViewModel.getTracingStatusInterface().wasContactReportedAsExposed();
- if (isOpenLeitfadenPending && isExposed) {
- gotoReportsFragment();
- }
- }
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(STATE_CONSUMED_EXPOSED_INTENT, consumedExposedIntent);
- outState.putBoolean(STATE_CONSUMED_COVIDCODE_INTENT, consumedCovidcodeIntent);
+ outState.putBoolean(KEY_IS_INTENT_CONSUMED, isIntentConsumed);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
- consumedCovidcodeIntent = false;
+ isIntentConsumed = false;
}
@Override
protected void onStart() {
super.onStart();
secureStorage.setAppOpenAfterNotificationPending(false);
+ crowdNotifierViewModel.refreshTraceKeys();
+ crowdNotifierViewModel.refreshErrors();
+ autoCheckoutIfNecessary(this, crowdNotifierViewModel.getCheckInState());
}
@Override
protected void onResume() {
super.onResume();
- if (secureStorage.getOnboardingCompleted()) checkValidCovidcodeIntent();
+ if (secureStorage.getOnboardingCompleted()) checkIntentForActions();
+ LocalBroadcastManager.getInstance(this)
+ .registerReceiver(autoCheckoutBroadcastReceiver, new IntentFilter(ACTION_DID_AUTO_CHECKOUT));
}
private void checkIntentForActions() {
Intent intent = getIntent();
- String intentAction = intent.getAction();
boolean launchedFromHistory = (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0;
- if (ACTION_INFORMED_GOTO_REPORTS.equals(intentAction) && !launchedFromHistory) {
+ if (!launchedFromHistory && !isIntentConsumed) {
+ isIntentConsumed = true;
+ handleCustomIntents();
+ }
+ }
+
+ private void handleCustomIntents() {
+ String intentAction = getIntent().getAction();
+ if (ACTION_INFORMED_GOTO_REPORTS.equals(intentAction)) {
secureStorage.setLeitfadenOpenPending(false);
secureStorage.setReportsHeaderAnimationPending(false);
- gotoReportsFragment();
- intent.setAction(null);
- setIntent(intent);
- } else if (ACTION_EXPOSED_GOTO_REPORTS.equals(intentAction) && !launchedFromHistory && !consumedExposedIntent) {
- consumedExposedIntent = true;
- intent.setAction(null);
- setIntent(intent);
+ showReportsFragment();
+ } else if (ACTION_EXPOSED_GOTO_REPORTS.equals(intentAction)) {
if (tracingViewModel.getTracingStatusInterface().wasContactReportedAsExposed()) {
- gotoReportsFragment();
+ showReportsFragment();
}
} else if (ACTION_ACTIVATE_TRACING.equals(intentAction)) {
tracingViewModel.enableTracing(this, () -> {}, e -> {}, () -> {});
+ } else if ((ACTION_CROWDNOTIFIER_REMINDER_NOTIFICATION.equals(intentAction) ||
+ ACTION_ONGOING_NOTIFICATION.equals(intentAction)) && crowdNotifierViewModel.isCheckedIn().getValue()) {
+ showCheckinOverviewFragment();
+ } else if (ACTION_CHECK_OUT_NOW.equals(intentAction) && crowdNotifierViewModel.isCheckedIn().getValue()) {
+ showCheckOutFragment();
+ } else if (ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION.equals(intentAction)) {
+ showReportsFragment();
+ } else if (getIntent().getData() != null) {
+ checkValidCovidcodeIntent();
+ checkValidCheckInIntent();
+ }
+ }
+
+ private void checkValidCheckInIntent() {
+ String qrCodeData = getIntent().getDataString();
+ try {
+ VenueInfo venueInfo = CrowdNotifier.getVenueInfo(qrCodeData, BuildConfig.ENTRY_QR_CODE_PREFIX);
+ if (crowdNotifierViewModel.isCheckedIn().getValue()) {
+ new ErrorDialog(this, CrowdNotifierErrorState.ALREADY_CHECKED_IN).show();
+ } else {
+ crowdNotifierViewModel.setCheckInState(new CheckInState(false, venueInfo, System.currentTimeMillis(),
+ System.currentTimeMillis(), ReminderOption.OFF));
+ showCheckInScreen();
+ }
+ } catch (QrUtils.QRException e) {
+ handleInvalidQRCodeExceptions(qrCodeData, e);
+ }
+ }
+
+ private void handleInvalidQRCodeExceptions(String qrCodeData, QrUtils.QRException e) {
+ if (e instanceof QrUtils.InvalidQRCodeVersionException) {
+ new ErrorDialog(this, CrowdNotifierErrorState.UPDATE_REQUIRED).show();
+ } else if (e instanceof QrUtils.NotYetValidException) {
+ new ErrorDialog(this, CrowdNotifierErrorState.QR_CODE_NOT_YET_VALID).show();
+ } else if (e instanceof QrUtils.NotValidAnymoreException) {
+ new ErrorDialog(this, CrowdNotifierErrorState.QR_CODE_NOT_VALID_ANYMORE).show();
+ } else {
+ if (qrCodeData.startsWith(BuildConfig.TRACE_QR_CODE_PREFIX)) {
+ Intent openBrowserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(qrCodeData));
+ startActivity(openBrowserIntent);
+ } else {
+ new ErrorDialog(this, CrowdNotifierErrorState.NO_VALID_QR_CODE).show();
+ }
}
}
+
private void checkValidCovidcodeIntent() {
- boolean launchedFromHistory = (getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0;
TracingStatusInterface tracingStatus = tracingViewModel.getAppStatusLiveData().getValue();
- if (getIntent().getData() == null || launchedFromHistory || tracingStatus == null || tracingStatus.isReportedAsInfected() ||
- consumedCovidcodeIntent) {
+ if (tracingStatus == null || tracingStatus.isReportedAsInfected()) {
return;
}
Uri uri = Uri.parse(getIntent().getData().toString());
@@ -189,7 +256,6 @@ private void checkValidCovidcodeIntent() {
String covidCode = uri.getFragment();
if (covidCode == null || covidCode.length() != 12) return;
startInformFlow(covidCode);
- consumedCovidcodeIntent = true;
}
private void startInformFlow(String covidCode) {
@@ -210,7 +276,7 @@ private void showHomeFragment() {
.commit();
}
- private void gotoReportsFragment() {
+ private void showReportsFragment() {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment_container, ReportsFragment.newInstance())
@@ -218,10 +284,39 @@ private void gotoReportsFragment() {
.commit();
}
+ private void showCheckinOverviewFragment() {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.container, CheckinOverviewFragment.newInstance())
+ .addToBackStack(CheckinOverviewFragment.class.getCanonicalName())
+ .commit();
+ }
+
+ private void showCheckOutFragment() {
+ getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter,
+ R.anim.modal_pop_exit)
+ .replace(R.id.container, CheckOutFragment.newInstance())
+ .addToBackStack(CheckOutFragment.class.getCanonicalName())
+ .commit();
+ }
+
+ private void showCheckInScreen() {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.container, CheckInFragment.newInstance())
+ .addToBackStack(CheckInFragment.class.getCanonicalName())
+ .commit();
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
DP3T.onActivityResult(this, requestCode, resultCode, data);
}
+ @Override
+ protected void onPause() {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(autoCheckoutBroadcastReceiver);
+ super.onPause();
+ }
+
}
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 ccc6431df..4139bd8d5 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
@@ -58,6 +58,7 @@ public void onReceive(Context context, Intent intent) {
public CrowdNotifierViewModel(@NonNull Application application) {
super(application);
+ refreshExposures();
storage = SecureStorage.getInstance(getApplication());
checkInState = storage.getCheckInState();
updateCheckedIn();
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
index 1b1640e03..23aa464ab 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
@@ -31,7 +31,7 @@ public class CheckOutFragment extends Fragment {
public final static String TAG = CheckOutFragment.class.getCanonicalName();
- private final static long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+ private final static long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L;
private CrowdNotifierViewModel viewModel;
private VenueInfo venueInfo;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
index f45ec3915..7eeb5d70e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
@@ -14,7 +14,6 @@
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
import ch.admin.bag.dp3t.storage.SecureStorage;
import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
-import ch.admin.bag.dp3t.util.ReminderHelper;
import ch.admin.bag.dp3t.checkin.models.CheckInState;
public class CrowdNotifierReminderHelper extends BroadcastReceiver {
@@ -80,7 +79,7 @@ private static void removeReminder(PendingIntent pendingIntent, Context context)
}
private static PendingIntent getPendingIntent(Context context, boolean eightHours) {
- Intent intent = new Intent(context, ReminderHelper.class);
+ Intent intent = new Intent(context, CrowdNotifierReminderHelper.class);
if (eightHours) {
intent.setAction(ACTION_EIGHT_HOUR_REMINDER);
return PendingIntent.getBroadcast(context, EIGHT_HOUR_REMINDER_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -91,7 +90,7 @@ private static PendingIntent getPendingIntent(Context context, boolean eightHour
}
private static PendingIntent getAutoCheckOutPendingIntent(Context context) {
- Intent intent = new Intent(context, ReminderHelper.class);
+ Intent intent = new Intent(context, CrowdNotifierReminderHelper.class);
intent.setAction(ACTION_AUTO_CHECKOUT);
return PendingIntent.getBroadcast(context, AUTO_CHECK_OUT_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
index 28beb363b..a1fef797b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
@@ -22,15 +22,17 @@
public class NotificationHelper {
- public static final String ACTION_EXPOSURE_NOTIFICATION = BuildConfig.APPLICATION_ID + ".EXPOSURE_NOTIFICATION_ACTION";
- public static final String ACTION_REMINDER_NOTIFICATION = BuildConfig.APPLICATION_ID + ".ACTION_REMINDER_NOTIFICATION";
+ public static final String ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION = BuildConfig.APPLICATION_ID +
+ ".ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION";
+ public static final String ACTION_CROWDNOTIFIER_REMINDER_NOTIFICATION = BuildConfig.APPLICATION_ID +
+ ".ACTION_CROWDNOTIFIER_REMINDER_NOTIFICATION";
public static final String ACTION_AUTO_CHECKOUT_NOTIFICATION = BuildConfig.APPLICATION_ID +
".ACTION_AUTO_CHECKOUT_NOTIFICATION";
public static final String ACTION_ONGOING_NOTIFICATION = BuildConfig.APPLICATION_ID + ".ACTION_ONGOING_NOTIFICATION";
public static final String ACTION_CHECK_OUT_NOW = BuildConfig.APPLICATION_ID + ".ACTION_CHECK_OUT_NOW";
public static final String ACTION_SNOOZE = BuildConfig.APPLICATION_ID + ".ACTION_SNOOZE";
- public static final String EXPOSURE_ID_EXTRA = "EXPOSURE_ID";
+ public static final String CROWDNOTIFIER_EXPOSURE_ID_EXTRA = "CROWDNOTIFIER_EXPOSURE_ID";
private final String CHANNEL_ID_EXPOSURE_NOTIFICATION = "ExposureNotificaitons";
private final String CHANNEL_ID_REMINDER = "Reminders";
@@ -79,12 +81,12 @@ private PendingIntent createBasicPendingIntent(String notificationAction) {
private PendingIntent createExposurePendingIntent(long exposureId) {
Intent intent = new Intent(context, MainActivity.class);
- intent.setAction(ACTION_EXPOSURE_NOTIFICATION);
- intent.putExtra(EXPOSURE_ID_EXTRA, exposureId);
+ intent.setAction(ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION);
+ intent.putExtra(CROWDNOTIFIER_EXPOSURE_ID_EXTRA, exposureId);
intent.setAction(Long.toString(exposureId));
return TaskStackBuilder.create(context)
.addNextIntentWithParentStack(intent)
- .getPendingIntent(ACTION_EXPOSURE_NOTIFICATION.hashCode(), PendingIntent.FLAG_ONE_SHOT);
+ .getPendingIntent(ACTION_CROWDNOTIFIER_EXPOSURE_NOTIFICATION.hashCode(), PendingIntent.FLAG_ONE_SHOT);
}
private NotificationCompat.Builder getNotificationBuilder(String channelId) {
@@ -133,7 +135,7 @@ public void showReminderNotification() {
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = getNotificationBuilder(CHANNEL_ID_REMINDER)
- .setContentIntent(createBasicPendingIntent(ACTION_REMINDER_NOTIFICATION))
+ .setContentIntent(createBasicPendingIntent(ACTION_CROWDNOTIFIER_REMINDER_NOTIFICATION))
.setContentTitle(context.getString(R.string.checkout_reminder_title))
.setContentText(context.getString(R.string.checkout_reminder_text))
.addAction(R.drawable.ic_close,
diff --git a/app/src/main/res/anim/modal_pop_enter.xml b/app/src/main/res/anim/modal_pop_enter.xml
new file mode 100644
index 000000000..e6d7ff503
--- /dev/null
+++ b/app/src/main/res/anim/modal_pop_enter.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/modal_pop_exit.xml b/app/src/main/res/anim/modal_pop_exit.xml
new file mode 100644
index 000000000..53a435703
--- /dev/null
+++ b/app/src/main/res/anim/modal_pop_exit.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/modal_slide_enter.xml b/app/src/main/res/anim/modal_slide_enter.xml
new file mode 100644
index 000000000..900ed44de
--- /dev/null
+++ b/app/src/main/res/anim/modal_slide_enter.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/app/src/main/res/anim/modal_slide_exit.xml b/app/src/main/res/anim/modal_slide_exit.xml
new file mode 100644
index 000000000..e97f753a3
--- /dev/null
+++ b/app/src/main/res/anim/modal_slide_exit.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
From c54f70daac6615f7c2843c08f57d4abf496b2db4 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 30 Apr 2021 10:14:51 +0200
Subject: [PATCH 013/207] Fix TraceKey load bug and add illu
---
.../networking/TraceKeysRepository.java | 7 +-
.../main/res/drawable/ic_illu_checked_in.xml | 229 ++++++++++++++++++
.../res/layout/fragment_checkin_overview.xml | 8 +
3 files changed, 242 insertions(+), 2 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_illu_checked_in.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index a318dff64..d2e795ca7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -74,8 +74,11 @@ public List loadTraceKeys() {
private List handleSuccessfulResponse(Response response) {
try {
- long keyBundleTag = Long.parseLong(response.headers().get(KEY_BUNDLE_TAG_HEADER));
- storage.setCrowdNotifierLastKeyBundleTag(keyBundleTag);
+ String keyBundleTag = response.headers().get(KEY_BUNDLE_TAG_HEADER);
+ if (keyBundleTag != null) {
+ long keyBundleTagValue = Long.parseLong(keyBundleTag);
+ storage.setCrowdNotifierLastKeyBundleTag(keyBundleTagValue);
+ }
Proto.ProblematicEventWrapper problematicEventWrapper =
Proto.ProblematicEventWrapper.parseFrom(response.body().byteStream());
ArrayList problematicEventInfos = new ArrayList<>();
diff --git a/app/src/main/res/drawable/ic_illu_checked_in.xml b/app/src/main/res/drawable/ic_illu_checked_in.xml
new file mode 100644
index 000000000..83ad70ca7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_illu_checked_in.xml
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
index 6708095bf..516ec643f 100644
--- a/app/src/main/res/layout/fragment_checkin_overview.xml
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -52,6 +52,13 @@
android:foreground="?selectableItemBackground"
android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_events_overview.xml b/app/src/main/res/layout/fragment_events_overview.xml
index da51a06c4..8c4d95f06 100644
--- a/app/src/main/res/layout/fragment_events_overview.xml
+++ b/app/src/main/res/layout/fragment_events_overview.xml
@@ -16,7 +16,7 @@
android:orientation="vertical">
Date: Mon, 3 May 2021 18:41:27 +0200
Subject: [PATCH 019/207] Implemented Storing of List of QR Codes
---
.../checkin/checkinflow/CheckInFragment.java | 4 +-
.../checkin/checkinflow/CheckOutFragment.java | 4 +-
.../checkin/events/EventsOverviewFragment.kt | 10 +-
.../bag/dp3t/checkin/events/QrCodeAdapter.kt | 18 +--
.../generateqrcode/GenerateQrCodeFragment.kt | 49 ++++----
.../GeneratedQrCodesSerializer.kt | 22 ++++
.../generateqrcode/QRCodePayloadSerializer.kt | 30 -----
.../checkin/generateqrcode/QRCodeViewModel.kt | 114 ++++++++++++------
.../networking/TraceKeysRepository.java | 7 +-
.../checkin/utils/NotificationHelper.java | 2 +-
.../checkin/utils/QrCodePayloadExtensions.kt | 10 ++
.../checkin/utils/VenueInfoExtensions.java | 31 -----
.../dp3t/checkin/utils/VenueInfoExtensions.kt | 31 +++++
app/src/main/proto/App.proto | 49 ++++++++
.../{Proto.proto => Crowdnotifier.proto} | 12 +-
.../res/layout/fragment_generate_qr_code.xml | 1 +
16 files changed, 232 insertions(+), 162 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GeneratedQrCodesSerializer.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodePayloadSerializer.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
create mode 100644 app/src/main/proto/App.proto
rename app/src/main/proto/{Proto.proto => Crowdnotifier.proto} (82%)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
index 7dbc616d7..8ffc3ceae 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.java
@@ -21,7 +21,7 @@
import ch.admin.bag.dp3t.checkin.models.ReminderOption;
import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper;
import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
-import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensions;
+import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensionsKt;
public class CheckInFragment extends Fragment {
@@ -68,7 +68,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
MaterialButtonToggleGroup toggleGroup = view.findViewById(R.id.check_in_fragment_toggle_group);
titleTextView.setText(venueInfo.getTitle());
- subtitleTextView.setText(VenueInfoExtensions.getSubtitle(venueInfo));
+ subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(venueInfo));
checkInButton.setOnClickListener(v -> {
long checkInTime = System.currentTimeMillis();
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
index b16d3ff5c..49adbc93d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
@@ -24,7 +24,7 @@
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper;
import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
-import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensions;
+import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensionsKt;
import ch.admin.bag.dp3t.util.StringUtil;
public class CheckOutFragment extends Fragment {
@@ -87,7 +87,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
dateTextView = view.findViewById(R.id.check_out_fragment_date);
titleTextView.setText(venueInfo.getTitle());
- subtitleTextView.setText(VenueInfoExtensions.getSubtitle(venueInfo));
+ subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(venueInfo));
checkInState.setCheckOutTime(System.currentTimeMillis());
refreshTimeTextViews();
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
index 03e7baca2..588e0e48c 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
@@ -6,12 +6,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Observer
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.generateqrcode.GenerateQrCodeFragment
import ch.admin.bag.dp3t.checkin.generateqrcode.QRCodeViewModel
import ch.admin.bag.dp3t.databinding.FragmentEventsOverviewBinding
-import org.crowdnotifier.android.sdk.model.v3.ProtoV3
+import org.crowdnotifier.android.sdk.model.VenueInfo
class EventsOverviewFragment : Fragment() {
@@ -46,19 +45,16 @@ class EventsOverviewFragment : Fragment() {
.commit()
}
- override fun onQrCodeClicked(qrCodeItem: ProtoV3.QRCodePayload) {
+ override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
//TODO show qr code
}
})
binding.qrList.adapter = adapter
adapter.setItems(emptyList())
- qrCodeViewModel.loadQrCodePayloads()
- qrCodeViewModel.qrCodeListStateLiveData.observe(viewLifecycleOwner, Observer {
+ qrCodeViewModel.generatedQrCodesLiveData.observe(viewLifecycleOwner, {
adapter.setItems(it)
})
-
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
index 298a384cf..63e7406f1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
@@ -6,7 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
import ch.admin.bag.dp3t.databinding.ItemGenerateQrCodeBinding
import ch.admin.bag.dp3t.databinding.ItemQrCodeBinding
import ch.admin.bag.dp3t.databinding.ItemQrCodeEmptyListBinding
-import org.crowdnotifier.android.sdk.model.v3.ProtoV3
+import org.crowdnotifier.android.sdk.model.VenueInfo
class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter>() {
@@ -18,7 +18,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
private var items: List = emptyList()
- fun setItems(qrCodes: List) {
+ fun setItems(qrCodes: List) {
val newItems: ArrayList = ArrayList()
newItems.add(GenerateQrCodeItem())
if (qrCodes.isEmpty()) {
@@ -54,7 +54,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
val item = items[position]
when (holder) {
is GenerateQrCodeViewHolder -> holder.bind(item as GenerateQrCodeItem)
- is QrCodeViewHolder -> holder.bind(item as ProtoV3.QRCodePayload)
+ is QrCodeViewHolder -> holder.bind(item as VenueInfo)
is QrCodeEmptyListViewHolder -> holder.bind(item as QrCodeEmptyListItem)
else -> throw IllegalArgumentException()
}
@@ -62,7 +62,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
- is ProtoV3.QRCodePayload -> TYPE_QR_CODE
+ is VenueInfo -> TYPE_QR_CODE
is GenerateQrCodeItem -> TYPE_GENERATE_QR_CODE
is QrCodeEmptyListItem -> TYPE_QR_EMPTY_LIST
else -> throw java.lang.IllegalArgumentException("Invalid type of data at position $position")
@@ -84,10 +84,10 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
}
inner class QrCodeViewHolder(private val binding: ItemQrCodeBinding) :
- QrCodeBaseViewHolder(binding.root) {
- override fun bind(item: ProtoV3.QRCodePayload) {
- binding.qrCodeName.text = item.locationData.description
- binding.qrCodeLocation.text = item.countryData.toString()
+ QrCodeBaseViewHolder(binding.root) {
+ override fun bind(item: VenueInfo) {
+ binding.qrCodeName.text = item.title
+ binding.qrCodeLocation.text = "TODO"
binding.root.setOnClickListener { onClickListener.onQrCodeClicked(item) }
}
@@ -103,7 +103,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
interface OnClickListener {
fun generateQrCode()
- fun onQrCodeClicked(qrCodeItem: ProtoV3.QRCodePayload)
+ fun onQrCodeClicked(qrCodeItem: VenueInfo)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
index 30772f492..7d1f96e11 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
@@ -1,5 +1,6 @@
package ch.admin.bag.dp3t.checkin.generateqrcode
+import android.content.Context
import android.content.res.ColorStateList
import android.graphics.BlendMode
import android.graphics.Color
@@ -9,25 +10,24 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
import android.widget.RadioButton
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Observer
+import ch.admin.bag.dp3t.checkin.models.VenueType
import ch.admin.bag.dp3t.databinding.FragmentGenerateQrCodeBinding
-import org.crowdnotifier.android.sdk.model.v3.ProtoV3
+
class GenerateQrCodeFragment : Fragment() {
companion object {
- fun newInstance(): GenerateQrCodeFragment {
- return GenerateQrCodeFragment()
- }
+ fun newInstance() = GenerateQrCodeFragment()
}
private lateinit var binding: FragmentGenerateQrCodeBinding
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentGenerateQrCodeBinding.inflate(layoutInflater)
return binding.root
}
@@ -35,10 +35,9 @@ class GenerateQrCodeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding.generateQrCodeCancel.setOnClickListener {
- requireActivity().supportFragmentManager.popBackStack()
- }
+ binding.generateQrCodeCancel.setOnClickListener { cancel() }
+ //TODO: Remove these hardcoded Events
val events = arrayListOf(EventType.PRIVATE_EVENT, EventType.MEETING_ROOM, EventType.OFFICE, EventType.OTHERS)
for (event in events) {
val radioButton = RadioButton(requireContext())
@@ -47,26 +46,20 @@ class GenerateQrCodeFragment : Fragment() {
binding.generateQrCodeRadioGroup.addView(radioButton)
}
- binding.qrCodeGenerate.setOnClickListener {
- val trac = ProtoV3.TraceLocation.newBuilder().setDescription("mau").build()
- val protobuff: ProtoV3.QRCodePayload =
- ProtoV3.QRCodePayload.newBuilder().setVersion(3)
- .setLocationData(trac).build()
- //TODO PP-966 for saving a list, we need a superior class which contains an list of QRCodePayloads
- //so we can use this approach https://stackoverflow.com/questions/64430872/how-to-save-a-list-of-objects-with-proto-datastore
- qrCodeViewModel.saveQRCodePayload(protobuff)
- }
+ binding.qrCodeGenerate.setOnClickListener { generateQrCode() }
+
+ }
+
+ private fun generateQrCode() {
+ //TODO: Set correct Venue Type
+ qrCodeViewModel.generateAndSaveQrCode(binding.titleEditText.text.toString(), VenueType.CAFETERIA)
+ cancel()
+ }
- qrCodeViewModel.qrCodeStateLiveData.observe(viewLifecycleOwner, Observer { state ->
- when (state) {
- is QrCodePayloadState.SUCCESS -> {
- requireActivity().supportFragmentManager.popBackStack()
- }
- is QrCodePayloadState.ERROR -> {
- //TODO PP-966 show error
- }
- }
- })
+ private fun cancel() {
+ requireActivity().supportFragmentManager.popBackStack()
+ val inputMethodManager = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputMethodManager.hideSoftInputFromWindow(binding.titleEditText.windowToken, 0)
}
enum class EventType(val value: String) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GeneratedQrCodesSerializer.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GeneratedQrCodesSerializer.kt
new file mode 100644
index 000000000..d9934a38a
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GeneratedQrCodesSerializer.kt
@@ -0,0 +1,22 @@
+package ch.admin.bag.dp3t.checkin.generateqrcode
+
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
+import ch.admin.bag.dp3t.checkin.models.GeneratedQrCodesWrapper
+import com.google.protobuf.InvalidProtocolBufferException
+import java.io.InputStream
+import java.io.OutputStream
+
+object GeneratedQrCodesSerializer : Serializer {
+ override val defaultValue: GeneratedQrCodesWrapper = GeneratedQrCodesWrapper.getDefaultInstance()
+
+ override suspend fun readFrom(input: InputStream): GeneratedQrCodesWrapper {
+ try {
+ return GeneratedQrCodesWrapper.parseFrom(input)
+ } catch (exception: InvalidProtocolBufferException) {
+ throw CorruptionException("Cannot read proto.", exception)
+ }
+ }
+
+ override suspend fun writeTo(t: GeneratedQrCodesWrapper, output: OutputStream) = t.writeTo(output)
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodePayloadSerializer.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodePayloadSerializer.kt
deleted file mode 100644
index 762ef0174..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodePayloadSerializer.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package ch.admin.bag.dp3t.checkin.generateqrcode
-
-import android.content.Context
-import androidx.datastore.core.CorruptionException
-import androidx.datastore.core.DataStore
-import androidx.datastore.core.Serializer
-import androidx.datastore.dataStore
-import com.google.protobuf.InvalidProtocolBufferException
-import org.crowdnotifier.android.sdk.model.v3.ProtoV3
-import java.io.InputStream
-import java.io.OutputStream
-
-object QRCodePayloadSerializer : Serializer {
- override val defaultValue: ProtoV3.QRCodePayload = ProtoV3.QRCodePayload.getDefaultInstance()
-
- override suspend fun readFrom(input: InputStream): ProtoV3.QRCodePayload {
- try {
- return ProtoV3.QRCodePayload.parseFrom(input)
- } catch (exception: InvalidProtocolBufferException) {
- throw CorruptionException("Cannot read proto.", exception)
- }
- }
-
- override suspend fun writeTo(t: ProtoV3.QRCodePayload, output: OutputStream) = t.writeTo(output)
-}
-
-val Context.qrCodePayloadDataStore: DataStore by dataStore(
- fileName = "qrCodePayloads.pb",
- serializer = QRCodePayloadSerializer
-)
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index 0a2b2ac00..4aa186c10 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -1,62 +1,96 @@
package ch.admin.bag.dp3t.checkin.generateqrcode
import android.app.Application
+import android.content.Context
+import android.util.Base64
import android.util.Log
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.collect
+import androidx.datastore.core.DataStore
+import androidx.datastore.dataStore
+import androidx.lifecycle.*
+import ch.admin.bag.dp3t.checkin.models.GeneratedQrCodesWrapper
+import ch.admin.bag.dp3t.checkin.models.SwissCovidLocationData
+import ch.admin.bag.dp3t.checkin.models.VenueType
+import ch.admin.bag.dp3t.checkin.utils.toQrCodePayload
+import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
-import org.crowdnotifier.android.sdk.model.v3.ProtoV3
+import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.crowdnotifier.android.sdk.model.v3.QRCodePayload
+import org.crowdnotifier.android.sdk.utils.Base64Util
import java.io.IOException
-class QRCodeViewModel(application: Application) : AndroidViewModel(application) {
+private const val MASTER_PUBLIC_KEY_BASE64 =
+ "TqRYigTM6YVO7/UJQuu3199mRqj0cSTp4DXCFlxbz9UqDLrASr07C9HJVWYtl08V7xGEGSSXWbQSRfRt/bqqDK0HQQGnZ/VWZxToo88txtgQ1ij7pYJwaBHAGGndLICL"
+private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
+private const val ONE_HOUR_IN_MILLIS = 60 * ONE_MINUTE_IN_MILLIS
+private const val AUTOMATIC_CHECKOUT_DELAY_MS = 12 * ONE_HOUR_IN_MILLIS
+private const val CHECKOUT_WARNING_DELAY_MS = 8 * ONE_HOUR_IN_MILLIS
+private const val SWISSCOVID_LOCATION_DATA_VERSION = 1
+private const val QR_CODE_VALIDITY_DURATION_MS = 10 * 365 * 24 * ONE_HOUR_IN_MILLIS // 10 years
+private val REMINDER_DELAY_OPTIONS_MS = listOf(30, 60, 120, 240).map { it * ONE_HOUR_IN_MILLIS } // 30, 60, 120 and 240 minutes
- private val qrCodeStateMutableLiveData: MutableLiveData = MutableLiveData()
- val qrCodeStateLiveData: LiveData = qrCodeStateMutableLiveData
+private val Context.generatedQrCodesDataStore: DataStore by dataStore(
+ fileName = "generatedQrCodes.pb",
+ serializer = GeneratedQrCodesSerializer
+)
- private val qrCodeListStateMutableLiveData: MutableLiveData> = MutableLiveData()
- val qrCodeListStateLiveData: LiveData> = qrCodeListStateMutableLiveData
+class QRCodeViewModel(application: Application) : AndroidViewModel(application) {
- fun saveQRCodePayload(qrCodePayload: ProtoV3.QRCodePayload) {
- viewModelScope.launch {
- try {
- val qrCodePayload = getApplication().qrCodePayloadDataStore.updateData {
- qrCodePayload
- }
- qrCodeStateMutableLiveData.postValue(QrCodePayloadState.SUCCESS(qrCodePayload))
- } catch (e: Exception) {
- qrCodeStateMutableLiveData.postValue(QrCodePayloadState.ERROR(e))
- }
+ private val generatedQrCodesFlow = getApplication().generatedQrCodesDataStore.data.buffer(1).catch { exception ->
+ if (exception is IOException) {
+ Log.e("QRCodeViewModel", "Error reading generated QR Codes.", exception)
+ } else {
+ throw exception
}
}
- fun loadQrCodePayloads() {
- viewModelScope.launch {
- val flow: Flow = getApplication().qrCodePayloadDataStore.data.catch { exception ->
- if (exception is IOException) {
- Log.e("QRCODEPayload", "Error reading sort order preferences.", exception)
- emit(ProtoV3.QRCodePayload.getDefaultInstance())
- } else {
- throw exception
- }
- }
-
- val arrayList = arrayListOf()
- flow.collect { payload ->
- arrayList.add(payload)
- qrCodeListStateMutableLiveData.postValue(arrayList.toList())
- }
+ val generatedQrCodesLiveData = generatedQrCodesFlow.map { wrapper ->
+ wrapper.generatedQrCodesList.map { it.toVenueInfo() }
+ }.asLiveData()
+
+
+ fun generateAndSaveQrCode(description: String, venueType: VenueType) = viewModelScope.launch {
+
+ val swissCovidLocationData = SwissCovidLocationData.newBuilder()
+ .setVersion(SWISSCOVID_LOCATION_DATA_VERSION)
+ .setAutomaticCheckoutDelaylMs(AUTOMATIC_CHECKOUT_DELAY_MS)
+ .setCheckoutWarningDelayMs(CHECKOUT_WARNING_DELAY_MS)
+ .addAllReminderDelayOptionsMs(REMINDER_DELAY_OPTIONS_MS)
+ .setTypeValue(venueType.number)
+ .build()
+
+ val generatedVenueInfo = CrowdNotifier.generateVenueInfo(
+ description,
+ "",
+ swissCovidLocationData.toByteArray(),
+ System.currentTimeMillis() / 1000,
+ (System.currentTimeMillis() + QR_CODE_VALIDITY_DURATION_MS) / 1000,
+ Base64.decode(MASTER_PUBLIC_KEY_BASE64, Base64.DEFAULT)
+ )
+
+ val newWrapper = GeneratedQrCodesWrapper.newBuilder()
+ .addAllGeneratedQrCodes(generatedQrCodesFlow.first().generatedQrCodesList)
+ .addGeneratedQrCodes(generatedVenueInfo.toQrCodePayload())
+ .build()
+ saveGeneratedVenueInfo(newWrapper)
+
+ }
+
+ private suspend fun saveGeneratedVenueInfo(generatedQrCodesWrapper: GeneratedQrCodesWrapper) {
+
+ try {
+ getApplication().generatedQrCodesDataStore.updateData { generatedQrCodesWrapper }
+ } catch (e: Exception) {
+ //TODO: Handle Error
}
+
}
+
}
sealed class QrCodePayloadState {
- data class SUCCESS(val qrCodePayload: ProtoV3.QRCodePayload) : QrCodePayloadState()
+ data class SUCCESS(val qrCodePayload: QRCodePayload) : QrCodePayloadState()
data class ERROR(val exception: java.lang.Exception) : QrCodePayloadState()
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index ef5724f3a..f2dc46e19 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -12,6 +12,8 @@
import org.dpppt.android.sdk.backend.UserAgentInterceptor;
import ch.admin.bag.dp3t.BuildConfig;
+import ch.admin.bag.dp3t.checkin.models.ProblematicEvent;
+import ch.admin.bag.dp3t.checkin.models.ProblematicEventWrapper;
import ch.admin.bag.dp3t.checkin.models.Proto;
import ch.admin.bag.dp3t.storage.SecureStorage;
import okhttp3.OkHttpClient;
@@ -80,10 +82,9 @@ private List handleSuccessfulResponse(Response problematicEventInfos = new ArrayList<>();
- for (Proto.ProblematicEvent event : problematicEventWrapper.getEventsList()) {
+ for (ProblematicEvent event : problematicEventWrapper.getEventsList()) {
problematicEventInfos.add(new ProblematicEventInfo(event.getIdentity().toByteArray(),
event.getSecretKeyForIdentity().toByteArray(),
event.getEncryptedAssociatedData().toByteArray(), event.getCipherTextNonce().toByteArray(),
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
index a1fef797b..3a213d5be 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
@@ -160,7 +160,7 @@ public void startOngoingNotification(long startTime, VenueInfo venueInfo) {
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(context.getString(R.string.ongoing_notification_title)
.replace("{TIME}", StringUtil.getHourMinuteTimeString(startTime, ":")))
- .setContentText(venueInfo.getTitle() + "\n" + VenueInfoExtensions.getSubtitle(venueInfo))
+ .setContentText(venueInfo.getTitle() + "\n" + VenueInfoExtensionsKt.getSubtitle(venueInfo))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.addAction(R.drawable.ic_close,
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
new file mode 100644
index 000000000..27769c2f4
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
@@ -0,0 +1,10 @@
+package ch.admin.bag.dp3t.checkin.utils
+
+import ch.admin.bag.dp3t.checkin.models.QRCodePayload
+import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.crowdnotifier.android.sdk.model.VenueInfo
+import org.crowdnotifier.android.sdk.utils.Base64Util
+
+fun QRCodePayload.toVenueInfo(): VenueInfo {
+ return CrowdNotifier.getVenueInfo("?v=3#" + Base64Util.toBase64(this.toByteArray()), "")
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
deleted file mode 100644
index e25e736d2..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package ch.admin.bag.dp3t.checkin.utils;
-
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import org.crowdnotifier.android.sdk.model.VenueInfo;
-
-import ch.admin.bag.dp3t.checkin.models.Proto;
-
-public class VenueInfoExtensions {
-
-
- public static Proto.SwissCovidLocationData getNotifyMeLocationData(VenueInfo venueInfo) {
- if (venueInfo.getCountryData() == null) return Proto.SwissCovidLocationData.newBuilder().build();
- try {
- return Proto.SwissCovidLocationData.parseFrom(venueInfo.getCountryData());
- } catch (InvalidProtocolBufferException e) {
- return Proto.SwissCovidLocationData.newBuilder().build();
- }
- }
-
- public static String getSubtitle(VenueInfo venueInfo) {
- Proto.SwissCovidLocationData notifyMeLocationData = getNotifyMeLocationData(venueInfo);
- if (notifyMeLocationData.getRoom() == null || notifyMeLocationData.getRoom().equals("")) {
- return venueInfo.getAddress();
- } else {
- return venueInfo.getAddress() + ", " + notifyMeLocationData.getRoom();
- }
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
new file mode 100644
index 000000000..bed63156b
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
@@ -0,0 +1,31 @@
+package ch.admin.bag.dp3t.checkin.utils
+
+import ch.admin.bag.dp3t.checkin.models.QRCodePayload
+import ch.admin.bag.dp3t.checkin.models.SwissCovidLocationData
+import com.google.protobuf.InvalidProtocolBufferException
+import org.crowdnotifier.android.sdk.model.VenueInfo
+
+fun VenueInfo.getSwissCovidLocationData(): SwissCovidLocationData {
+ return if (countryData == null) {
+ SwissCovidLocationData.newBuilder().build()
+ } else {
+ try {
+ SwissCovidLocationData.parseFrom(countryData)
+ } catch (e: InvalidProtocolBufferException) {
+ SwissCovidLocationData.newBuilder().build()
+ }
+ }
+}
+
+fun VenueInfo.getSubtitle(): String {
+ //TODO: This is to be defined what the subtitle will be for SwissCovid
+ return ""
+}
+
+fun VenueInfo.toQrCodePayload(): QRCodePayload {
+ try {
+ return QRCodePayload.parseFrom(qrCodePayload)
+ } catch (e: InvalidProtocolBufferException) {
+ throw RuntimeException("VenueInfo contains invalid qrCodePayload bytes!")
+ }
+}
diff --git a/app/src/main/proto/App.proto b/app/src/main/proto/App.proto
new file mode 100644
index 000000000..57b19ad8a
--- /dev/null
+++ b/app/src/main/proto/App.proto
@@ -0,0 +1,49 @@
+syntax = "proto3";
+
+option java_package = "ch.admin.bag.dp3t.checkin.models";
+option java_multiple_files = true;
+
+message GeneratedQrCodesWrapper{
+ repeated QRCodePayload generatedQrCodes = 1;
+}
+
+message QRCodePayload {
+ uint32 version = 1;
+ TraceLocation locationData = 2;
+ CrowdNotifierData crowdNotifierData = 3;
+
+ bytes countryData = 4;
+}
+
+message TraceLocation {
+ uint32 version = 1;
+
+ // max. 100 characters
+ string description = 2;
+
+ // max. 100 characters
+ string address = 3;
+
+ // UNIX timestamp (in seconds)
+ uint64 startTimestamp = 4;
+
+ // UNIX timestamp (in seconds)
+ uint64 endTimestamp = 5;
+}
+
+message CrowdNotifierData {
+ uint32 version = 1;
+ bytes publicKey = 2;
+ bytes cryptographicSeed = 3;
+
+ // Ignore ;)
+ uint32 type = 4; // exact semantic tbd
+}
+
+message AssociatedData {
+ int32 version = 1;
+ string message = 2;
+ int64 startTimestamp = 3;
+ int64 endTimestamp = 4;
+ bytes countryData = 5;
+}
diff --git a/app/src/main/proto/Proto.proto b/app/src/main/proto/Crowdnotifier.proto
similarity index 82%
rename from app/src/main/proto/Proto.proto
rename to app/src/main/proto/Crowdnotifier.proto
index 51c02fa6b..4b9c2ecc4 100644
--- a/app/src/main/proto/Proto.proto
+++ b/app/src/main/proto/Crowdnotifier.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
-package ch.admin.bag.dp3t.checkin.models;
+
+option java_package = "ch.admin.bag.dp3t.checkin.models";
+option java_multiple_files = true;
message ProblematicEventWrapper {
int32 version = 1;
@@ -15,14 +17,6 @@ message ProblematicEvent {
bytes cipherTextNonce = 6;
}
-message AssociatedData {
- int32 version = 1;
- string message = 2;
- int64 startTime = 3;
- int64 endTime = 4;
- bytes countryData = 5;
-}
-
enum EventCriticality {
LOW = 0;
HIGH = 1;
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 7615aa690..d56490974 100644
--- a/app/src/main/res/layout/fragment_generate_qr_code.xml
+++ b/app/src/main/res/layout/fragment_generate_qr_code.xml
@@ -64,6 +64,7 @@
android:textAllCaps="true" />
Date: Tue, 4 May 2021 09:37:56 +0200
Subject: [PATCH 020/207] Implement deleting of generated QR Codes
---
.../checkin/events/EventsOverviewFragment.kt | 24 ++++++++++++-------
.../bag/dp3t/checkin/events/QrCodeAdapter.kt | 11 +++++----
.../checkin/generateqrcode/QRCodeViewModel.kt | 11 +++++++++
.../networking/TraceKeysRepository.java | 1 -
4 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
index 588e0e48c..0de7b368f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
@@ -32,29 +32,35 @@ class EventsOverviewFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding.eventsToolbar.setNavigationOnClickListener {
- requireActivity().supportFragmentManager.popBackStack()
- }
+ binding.eventsToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
val adapter = QrCodeAdapter(object : OnClickListener {
override fun generateQrCode() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, GenerateQrCodeFragment.newInstance())
- .addToBackStack(GenerateQrCodeFragment::class.java.canonicalName)
- .commit()
+ showGenerateQrCodeScreen()
}
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
//TODO show qr code
}
+ override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
+ qrCodeViewModel.deleteQrCode(qrCodeItem)
+ }
+
})
+
binding.qrList.adapter = adapter
- adapter.setItems(emptyList())
qrCodeViewModel.generatedQrCodesLiveData.observe(viewLifecycleOwner, {
adapter.setItems(it)
})
}
+ private fun showGenerateQrCodeScreen() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, GenerateQrCodeFragment.newInstance())
+ .addToBackStack(GenerateQrCodeFragment::class.java.canonicalName)
+ .commit()
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
index 63e7406f1..982a5731a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
@@ -86,11 +86,13 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
inner class QrCodeViewHolder(private val binding: ItemQrCodeBinding) :
QrCodeBaseViewHolder(binding.root) {
override fun bind(item: VenueInfo) {
- binding.qrCodeName.text = item.title
- binding.qrCodeLocation.text = "TODO"
- binding.root.setOnClickListener { onClickListener.onQrCodeClicked(item) }
+ binding.apply {
+ qrCodeName.text = item.title
+ qrCodeLocation.text = "TODO"
+ root.setOnClickListener { onClickListener.onQrCodeClicked(item) }
+ qrCodeDelete.setOnClickListener { onClickListener.onDeleteQrCodeClicked(item) }
+ }
}
-
}
inner class QrCodeEmptyListViewHolder(binding: ItemQrCodeEmptyListBinding) :
@@ -104,6 +106,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
interface OnClickListener {
fun generateQrCode()
fun onQrCodeClicked(qrCodeItem: VenueInfo)
+ fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index 4aa186c10..e41fc78a4 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -15,6 +15,7 @@ import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.crowdnotifier.android.sdk.model.VenueInfo
import org.crowdnotifier.android.sdk.model.v3.QRCodePayload
import org.crowdnotifier.android.sdk.utils.Base64Util
import java.io.IOException
@@ -50,6 +51,16 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
}.asLiveData()
+ fun deleteQrCode(venueInfo: VenueInfo) = viewModelScope.launch {
+ val builder = GeneratedQrCodesWrapper.newBuilder()
+ generatedQrCodesFlow.first().generatedQrCodesList.forEach {
+ if (venueInfo.toQrCodePayload() != it) {
+ builder.addGeneratedQrCodes(it)
+ }
+ }
+ saveGeneratedVenueInfo(builder.build())
+ }
+
fun generateAndSaveQrCode(description: String, venueType: VenueType) = viewModelScope.launch {
val swissCovidLocationData = SwissCovidLocationData.newBuilder()
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index f2dc46e19..98f9aee6f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -14,7 +14,6 @@
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.checkin.models.ProblematicEvent;
import ch.admin.bag.dp3t.checkin.models.ProblematicEventWrapper;
-import ch.admin.bag.dp3t.checkin.models.Proto;
import ch.admin.bag.dp3t.storage.SecureStorage;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
From e022eb1e14e0e63a7ecb9a28666071a63c616db0 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 4 May 2021 11:26:15 +0200
Subject: [PATCH 021/207] Implement QR Code Display
---
.../checkin/events/EventsOverviewFragment.kt | 11 ++-
.../checkin/generateqrcode/QrCodeFragment.kt | 31 ++++++
app/src/main/res/drawable/ic_check_in.xml | 10 ++
app/src/main/res/drawable/ic_print_pdf.xml | 10 ++
app/src/main/res/drawable/ic_share.xml | 10 ++
app/src/main/res/layout/fragment_qr_code.xml | 96 +++++++++++++++++++
6 files changed, 167 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
create mode 100644 app/src/main/res/drawable/ic_check_in.xml
create mode 100644 app/src/main/res/drawable/ic_print_pdf.xml
create mode 100644 app/src/main/res/drawable/ic_share.xml
create mode 100644 app/src/main/res/layout/fragment_qr_code.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
index 0de7b368f..509e1e6ad 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
@@ -9,6 +9,7 @@ import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.generateqrcode.GenerateQrCodeFragment
import ch.admin.bag.dp3t.checkin.generateqrcode.QRCodeViewModel
+import ch.admin.bag.dp3t.checkin.generateqrcode.QrCodeFragment
import ch.admin.bag.dp3t.databinding.FragmentEventsOverviewBinding
import org.crowdnotifier.android.sdk.model.VenueInfo
@@ -40,7 +41,7 @@ class EventsOverviewFragment : Fragment() {
}
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
- //TODO show qr code
+ showQrCodeFragment()
}
override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
@@ -55,6 +56,14 @@ class EventsOverviewFragment : Fragment() {
})
}
+ private fun showQrCodeFragment() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, QrCodeFragment.newInstance())
+ .addToBackStack(QrCodeFragment::class.java.canonicalName)
+ .commit()
+ }
+
private fun showGenerateQrCodeScreen() {
requireActivity().supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
new file mode 100644
index 000000000..e42a18670
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -0,0 +1,31 @@
+package ch.admin.bag.dp3t.checkin.generateqrcode
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
+import org.crowdnotifier.android.sdk.model.VenueInfo
+
+class QrCodeFragment : Fragment() {
+
+ companion object {
+ fun newInstance(venueInfo: VenueInfo) = QrCodeFragment().apply {
+ arguments = bundleOf("fef" to venueInfo)
+ }
+ }
+
+ private lateinit var binding: FragmentQrCodeBinding
+
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return FragmentQrCodeBinding.inflate(layoutInflater).apply {
+ cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ val venueInfo = arguments?.get("fef") as VenueInfo
+ }.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_check_in.xml b/app/src/main/res/drawable/ic_check_in.xml
new file mode 100644
index 000000000..4c60e940d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_check_in.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_print_pdf.xml b/app/src/main/res/drawable/ic_print_pdf.xml
new file mode 100644
index 000000000..4175e1d9d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_print_pdf.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml
new file mode 100644
index 000000000..35a3e5a67
--- /dev/null
+++ b/app/src/main/res/drawable/ic_share.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
new file mode 100644
index 000000000..6bbb157b2
--- /dev/null
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 1fb077ac3dd0833f670972100fa4f1138e05a8df Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 4 May 2021 15:22:14 +0200
Subject: [PATCH 022/207] Implemented QR Code PDF Generation
---
app/src/main/AndroidManifest.xml | 10 +++
.../checkin/events/EventsOverviewFragment.kt | 5 +-
.../generateqrcode/PdfPrintDocumentAdapter.kt | 63 +++++++++++++++
.../checkin/generateqrcode/QRCodeViewModel.kt | 60 ++++++++++++---
.../checkin/generateqrcode/QrCodeFragment.kt | 77 +++++++++++++++++--
.../bag/dp3t/checkin/utils/SingleLiveEvent.kt | 58 ++++++++++++++
app/src/main/res/layout/fragment_qr_code.xml | 29 +++++--
app/src/main/res/xml/provider_paths.xml | 4 +
8 files changed, 280 insertions(+), 26 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/PdfPrintDocumentAdapter.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/SingleLiveEvent.kt
create mode 100644 app/src/main/res/xml/provider_paths.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2324fbe37..ab7b259d9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -83,6 +83,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
index 509e1e6ad..ec5eac9a9 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
@@ -41,7 +41,7 @@ class EventsOverviewFragment : Fragment() {
}
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
- showQrCodeFragment()
+ showQrCodeFragment(qrCodeItem)
}
override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
@@ -56,7 +56,8 @@ class EventsOverviewFragment : Fragment() {
})
}
- private fun showQrCodeFragment() {
+ private fun showQrCodeFragment(venueInfo: VenueInfo) {
+ qrCodeViewModel.selectedQrCode = venueInfo
requireActivity().supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(R.id.main_fragment_container, QrCodeFragment.newInstance())
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/PdfPrintDocumentAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/PdfPrintDocumentAdapter.kt
new file mode 100644
index 000000000..ddcdd8268
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/PdfPrintDocumentAdapter.kt
@@ -0,0 +1,63 @@
+package ch.admin.bag.dp3t.checkin.generateqrcode
+
+import android.os.Bundle
+import android.os.CancellationSignal
+import android.os.ParcelFileDescriptor
+import android.print.PageRange
+import android.print.PrintAttributes
+import android.print.PrintDocumentAdapter
+import android.print.PrintDocumentInfo
+import android.util.Log
+import java.io.*
+
+class PdfPrintDocumentAdapter(private val file: File) : PrintDocumentAdapter() {
+
+ override fun onLayout(
+ oldAttributes: PrintAttributes?,
+ newAttributes: PrintAttributes?,
+ cancellationSignal: CancellationSignal,
+ callback: LayoutResultCallback,
+ extras: Bundle?
+ ) {
+ if (cancellationSignal.isCanceled) {
+ callback.onLayoutCancelled()
+ return
+ }
+
+ val info = PrintDocumentInfo.Builder("swisscovid-qr-code.pdf")
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
+ .build()
+
+ callback.onLayoutFinished(info, oldAttributes != newAttributes)
+ }
+
+ override fun onWrite(
+ pages: Array,
+ destination: ParcelFileDescriptor,
+ cancellationSignal: CancellationSignal,
+ callback: WriteResultCallback
+ ) {
+ var inputStream: InputStream? = null
+ var outputStream: OutputStream? = null
+
+ try {
+ inputStream = FileInputStream(file)
+ outputStream = FileOutputStream(destination.fileDescriptor)
+
+ inputStream.copyTo(outputStream)
+
+ if (cancellationSignal.isCanceled) {
+ callback.onWriteCancelled()
+ } else {
+ callback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
+ }
+ } catch (ex: Exception) {
+ callback.onWriteFailed(ex.message)
+ Log.e("PDFDocumentAdapter", "Could not write: ${ex.localizedMessage}")
+ } finally {
+ inputStream?.close()
+ outputStream?.close()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index e41fc78a4..c84fc1371 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -2,6 +2,9 @@ package ch.admin.bag.dp3t.checkin.generateqrcode
import android.app.Application
import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Paint
+import android.graphics.pdf.PdfDocument
import android.util.Base64
import android.util.Log
import androidx.datastore.core.DataStore
@@ -10,16 +13,19 @@ import androidx.lifecycle.*
import ch.admin.bag.dp3t.checkin.models.GeneratedQrCodesWrapper
import ch.admin.bag.dp3t.checkin.models.SwissCovidLocationData
import ch.admin.bag.dp3t.checkin.models.VenueType
+import ch.admin.bag.dp3t.checkin.utils.SingleLiveEvent
import ch.admin.bag.dp3t.checkin.utils.toQrCodePayload
import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.crowdnotifier.android.sdk.CrowdNotifier
import org.crowdnotifier.android.sdk.model.VenueInfo
-import org.crowdnotifier.android.sdk.model.v3.QRCodePayload
-import org.crowdnotifier.android.sdk.utils.Base64Util
+import java.io.File
+import java.io.FileOutputStream
import java.io.IOException
+
private const val MASTER_PUBLIC_KEY_BASE64 =
"TqRYigTM6YVO7/UJQuu3199mRqj0cSTp4DXCFlxbz9UqDLrASr07C9HJVWYtl08V7xGEGSSXWbQSRfRt/bqqDK0HQQGnZ/VWZxToo88txtgQ1ij7pYJwaBHAGGndLICL"
private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
@@ -29,6 +35,8 @@ private const val CHECKOUT_WARNING_DELAY_MS = 8 * ONE_HOUR_IN_MILLIS
private const val SWISSCOVID_LOCATION_DATA_VERSION = 1
private const val QR_CODE_VALIDITY_DURATION_MS = 10 * 365 * 24 * ONE_HOUR_IN_MILLIS // 10 years
private val REMINDER_DELAY_OPTIONS_MS = listOf(30, 60, 120, 240).map { it * ONE_HOUR_IN_MILLIS } // 30, 60, 120 and 240 minutes
+private const val QR_CODE_PIXEL_SIZE = 1000
+const val QR_CODE_PDF_FILE_NAME = "swisscovid-qr-code.pdf"
private val Context.generatedQrCodesDataStore: DataStore by dataStore(
fileName = "generatedQrCodes.pb",
@@ -37,7 +45,6 @@ private val Context.generatedQrCodesDataStore: DataStore().generatedQrCodesDataStore.data.buffer(1).catch { exception ->
if (exception is IOException) {
Log.e("QRCodeViewModel", "Error reading generated QR Codes.", exception)
@@ -50,6 +57,15 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
wrapper.generatedQrCodesList.map { it.toVenueInfo() }
}.asLiveData()
+ var selectedQrCode: VenueInfo? = null
+ set(value) {
+ selectedQrCodeBitmap.value = null
+ value?.let { generateQrCodeBitmap(it) }
+ field = value
+ }
+
+ val selectedQrCodeBitmap = MutableLiveData()
+ val selectedQrCodePdf = SingleLiveEvent()
fun deleteQrCode(venueInfo: VenueInfo) = viewModelScope.launch {
val builder = GeneratedQrCodesWrapper.newBuilder()
@@ -58,7 +74,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
builder.addGeneratedQrCodes(it)
}
}
- saveGeneratedVenueInfo(builder.build())
+ saveGeneratedQrCode(builder.build())
}
fun generateAndSaveQrCode(description: String, venueType: VenueType) = viewModelScope.launch {
@@ -85,23 +101,43 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
.addAllGeneratedQrCodes(generatedQrCodesFlow.first().generatedQrCodesList)
.addGeneratedQrCodes(generatedVenueInfo.toQrCodePayload())
.build()
- saveGeneratedVenueInfo(newWrapper)
+ saveGeneratedQrCode(newWrapper)
+ }
+
+ fun createQrCodePdf() = viewModelScope.launch(Dispatchers.IO) {
+
+ val directory = File(getApplication().externalCacheDir, "pdfs").apply { if (!exists()) mkdirs() }
+ val file = File(directory, QR_CODE_PDF_FILE_NAME)
+
+ val document = PdfDocument()
+ val pageInfo = PdfDocument.PageInfo.Builder(1240, 1748, 1).create()
+ val page = document.startPage(pageInfo)
+ val canvas = page.canvas
+ selectedQrCodeBitmap.value?.let {
+ canvas.drawBitmap(it, 120f, 120f, Paint())
+ }
+
+ document.finishPage(page)
+ FileOutputStream(file).use {
+ document.writeTo(it)
+ document.close()
+ }
+
+ selectedQrCodePdf.postValue(file)
}
- private suspend fun saveGeneratedVenueInfo(generatedQrCodesWrapper: GeneratedQrCodesWrapper) {
+ private suspend fun saveGeneratedQrCode(generatedQrCodesWrapper: GeneratedQrCodesWrapper) {
try {
getApplication().generatedQrCodesDataStore.updateData { generatedQrCodesWrapper }
} catch (e: Exception) {
- //TODO: Handle Error
+ throw RuntimeException(e)
}
-
}
-}
+ private fun generateQrCodeBitmap(venueInfo: VenueInfo) = viewModelScope.launch(Dispatchers.IO) {
+ selectedQrCodeBitmap.postValue(QrCode.create(venueInfo.toQrCodeString()).renderToBitmap(QR_CODE_PIXEL_SIZE))
+ }
-sealed class QrCodePayloadState {
- data class SUCCESS(val qrCodePayload: QRCodePayload) : QrCodePayloadState()
- data class ERROR(val exception: java.lang.Exception) : QrCodePayloadState()
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index e42a18670..39efc2fae 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -1,31 +1,94 @@
package ch.admin.bag.dp3t.checkin.generateqrcode
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
import android.os.Bundle
+import android.print.PrintAttributes
+import android.print.PrintManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.os.bundleOf
+import androidx.core.content.FileProvider
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
-import org.crowdnotifier.android.sdk.model.VenueInfo
+import java.io.File
+
class QrCodeFragment : Fragment() {
companion object {
- fun newInstance(venueInfo: VenueInfo) = QrCodeFragment().apply {
- arguments = bundleOf("fef" to venueInfo)
- }
+ fun newInstance() = QrCodeFragment()
}
- private lateinit var binding: FragmentQrCodeBinding
+ private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- val venueInfo = arguments?.get("fef") as VenueInfo
+ qrCodeViewModel.selectedQrCode?.let { venueInfo ->
+ titleTextview.text = venueInfo.title
+ subtitleTextview.text = "TODO"
+ qrCodeViewModel.selectedQrCodeBitmap.observe(viewLifecycleOwner) {
+ if (it == null) {
+ qrCodeImageview.visibility = View.INVISIBLE
+ qrCodeLoadingProgressbar.isVisible = true
+ shareButton.isEnabled = false
+ printPdfButton.isEnabled = false
+ } else {
+ qrCodeImageview.visibility = View.VISIBLE
+ qrCodeLoadingProgressbar.isVisible = false
+ qrCodeImageview.setImageBitmap(it)
+ shareButton.isEnabled = true
+ printPdfButton.isEnabled = true
+ }
+ }
+ shareButton.setOnClickListener { generatePdf(isPrint = false) }
+ printPdfButton.setOnClickListener { generatePdf(isPrint = true) }
+ deleteButton.setOnClickListener {
+ qrCodeViewModel.deleteQrCode(venueInfo)
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ }
}.root
}
+ private fun generatePdf(isPrint: Boolean) {
+ val pdf = qrCodeViewModel.selectedQrCodePdf.value
+ if (pdf == null) {
+ qrCodeViewModel.selectedQrCodePdf.observe(viewLifecycleOwner) {
+ if (isPrint) printPdf(it) else sharePdf(it)
+ }
+ qrCodeViewModel.createQrCodePdf()
+ } else {
+ if (isPrint) printPdf(pdf) else sharePdf(pdf)
+ }
+ }
+
+ private fun printPdf(file: File) {
+ context?.let {
+ val manager = it.getSystemService(Context.PRINT_SERVICE) as PrintManager
+ val adapter = PdfPrintDocumentAdapter(file)
+ val attributes = PrintAttributes.Builder().build()
+ manager.print("SwissCovid QR Code", adapter, attributes)
+ }
+ }
+
+ private fun sharePdf(file: File) {
+ context?.let {
+ val pdfUri: Uri = FileProvider.getUriForFile(it, it.applicationContext.packageName.toString() + ".provider", file)
+
+ Intent().apply {
+ action = Intent.ACTION_SEND
+ type = "application/pdf"
+ putExtra(Intent.EXTRA_STREAM, pdfUri)
+ startActivity(this)
+ }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/SingleLiveEvent.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/SingleLiveEvent.kt
new file mode 100644
index 000000000..7bbb652c1
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/SingleLiveEvent.kt
@@ -0,0 +1,58 @@
+package ch.admin.bag.dp3t.checkin.utils
+
+import android.util.Log
+import androidx.annotation.MainThread
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Observer
+import java.util.concurrent.atomic.AtomicBoolean
+
+private const val TAG = "SingleLiveEvent"
+
+/**
+ * A lifecycle-aware observable that sends only new updates after subscription, used for events like
+ * navigation and Snackbar messages.
+ *
+ *
+ * This avoids a common problem with events: on configuration change (like rotation) an update
+ * can be emitted if the observer is active. This LiveData only calls the observable if there's an
+ * explicit call to setValue() or call().
+ *
+ *
+ * Note that only one observer is going to be notified of changes.
+ *
+ * Inspired by https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java
+ */
+class SingleLiveEvent : MutableLiveData() {
+
+ // flag to avoid calling Observer's `onChanged()` when it's registered
+ private val mPending = AtomicBoolean(false)
+
+ @MainThread
+ override fun observe(owner: LifecycleOwner, observer: Observer) {
+ if (hasActiveObservers()) {
+ Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
+ }
+
+ // Observe the internal MutableLiveData
+ super.observe(owner, { t ->
+ if (mPending.compareAndSet(true, false)) {
+ observer.onChanged(t)
+ }
+ })
+ }
+
+ @MainThread
+ override fun setValue(t: T) {
+ mPending.set(true)
+ super.setValue(t)
+ }
+}
+
+/**
+ * Used for cases where T is Unit, to make calls cleaner.
+ */
+@MainThread
+fun SingleLiveEvent.call() {
+ setValue(null)
+}
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
index 6bbb157b2..14c5bb822 100644
--- a/app/src/main/res/layout/fragment_qr_code.xml
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -1,5 +1,6 @@
-
+ android:layout_margin="@dimen/spacing_larger">
+
+
+
+
+
+
+
+
+
From a275e7da8e9eb2ae0bc9383f4e9d7ade87f642ff Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 4 May 2021 17:31:18 +0200
Subject: [PATCH 023/207] Clean ups
---
app/build.gradle | 2 +-
.../dp3t/checkin/CheckinOverviewFragment.kt | 55 ++++++++-----------
.../checkin/events/EventsOverviewFragment.kt | 42 ++++++--------
.../generateqrcode/GenerateQrCodeFragment.kt | 39 ++++++-------
.../checkin/generateqrcode/QRCodeViewModel.kt | 7 ++-
.../checkin/generateqrcode/QrCodeFragment.kt | 1 -
.../checkin/utils/QrCodePayloadExtensions.kt | 4 +-
.../res/layout/fragment_events_overview.xml | 2 +-
.../res/layout/fragment_generate_qr_code.xml | 10 ++--
app/src/main/res/layout/fragment_qr_code.xml | 1 +
.../main/res/layout/item_generate_qr_code.xml | 2 +-
.../res/layout/item_qr_code_empty_list.xml | 2 +-
app/src/main/res/values/strings.xml | 7 +--
13 files changed, 78 insertions(+), 96 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index d23bfe1df..700cf2f49 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -207,7 +207,7 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-6'
+ implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-7'
implementation 'androidx.appcompat:appcompat:1.2.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 8dbfb57dc..f4439d1ac 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
@@ -23,46 +23,39 @@ class CheckinOverviewFragment : Fragment() {
}
}
- private lateinit var binding: FragmentCheckinOverviewBinding
-
private val crowdNotifierViewModel: CrowdNotifierViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- binding = FragmentCheckinOverviewBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- binding.checkinOverviewToolbar.setNavigationOnClickListener { v ->
- requireActivity().supportFragmentManager.popBackStack()
- }
+ return FragmentCheckinOverviewBinding.inflate(layoutInflater).apply {
+ checkinOverviewToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- binding.qrCodeGenerate.setOnClickListener {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, EventsOverviewFragment.newInstance())
- .addToBackStack(EventsOverviewFragment::class.java.canonicalName)
- .commit()
- }
- binding.checkinOverviewHistory.setOnClickListener { v: View? -> }
+ qrCodeGenerate.setOnClickListener { showEventsOverviewFragment() }
+ checkinOverviewHistory.setOnClickListener { v: View? -> }
- crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner, { isCheckedIn ->
- binding.checkoutView.isVisible = isCheckedIn
- binding.checkinView.isVisible = !isCheckedIn
- if (isCheckedIn) {
- crowdNotifierViewModel.startCheckInTimer()
- }
- })
+ crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner, { isCheckedIn ->
+ checkoutView.isVisible = isCheckedIn
+ checkinView.isVisible = !isCheckedIn
+ if (isCheckedIn) {
+ crowdNotifierViewModel.startCheckInTimer()
+ }
+ })
- binding.checkinButton.setOnClickListener { v -> showQrCodeScannerFragment() }
- binding.checkoutButton.setOnClickListener { v -> showCheckOutFragment() }
+ checkinButton.setOnClickListener { showQrCodeScannerFragment() }
+ checkoutButton.setOnClickListener { showCheckOutFragment() }
+ crowdNotifierViewModel.timeSinceCheckIn.observe(viewLifecycleOwner) { duration ->
+ checkinTime.text = StringUtil.getShortDurationString(duration)
+ }
+ }.root
+ }
- crowdNotifierViewModel.timeSinceCheckIn.observe(
- viewLifecycleOwner,
- { duration -> binding.checkinTime.text = StringUtil.getShortDurationString(duration) })
+ private fun showEventsOverviewFragment() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, EventsOverviewFragment.newInstance())
+ .addToBackStack(EventsOverviewFragment::class.java.canonicalName)
+ .commit()
}
private fun showCheckOutFragment() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
index ec5eac9a9..a34518013 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
@@ -21,39 +21,33 @@ class EventsOverviewFragment : Fragment() {
}
}
- private lateinit var binding: FragmentEventsOverviewBinding
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- binding = FragmentEventsOverviewBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
+ return FragmentEventsOverviewBinding.inflate(layoutInflater).apply {
+ eventsToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- binding.eventsToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ val adapter = QrCodeAdapter(object : OnClickListener {
+ override fun generateQrCode() {
+ showGenerateQrCodeScreen()
+ }
- val adapter = QrCodeAdapter(object : OnClickListener {
- override fun generateQrCode() {
- showGenerateQrCodeScreen()
- }
+ override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
+ showQrCodeFragment(qrCodeItem)
+ }
- override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
- showQrCodeFragment(qrCodeItem)
- }
+ override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
+ qrCodeViewModel.deleteQrCode(qrCodeItem)
+ }
- override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
- qrCodeViewModel.deleteQrCode(qrCodeItem)
- }
+ })
- })
+ qrList.adapter = adapter
+ qrCodeViewModel.generatedQrCodesLiveData.observe(viewLifecycleOwner, {
+ adapter.setItems(it)
+ })
- binding.qrList.adapter = adapter
- qrCodeViewModel.generatedQrCodesLiveData.observe(viewLifecycleOwner, {
- adapter.setItems(it)
- })
+ }.root
}
private fun showQrCodeFragment(venueInfo: VenueInfo) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
index 7d1f96e11..175cc9237 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
@@ -28,31 +28,28 @@ class GenerateQrCodeFragment : Fragment() {
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- binding = FragmentGenerateQrCodeBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- binding.generateQrCodeCancel.setOnClickListener { cancel() }
+ binding = FragmentGenerateQrCodeBinding.inflate(layoutInflater).apply {
+ generateQrCodeCancel.setOnClickListener { cancel() }
+
+ //TODO: Remove these hardcoded Events
+ val events = arrayListOf(EventType.PRIVATE_EVENT, EventType.MEETING_ROOM, EventType.OFFICE, EventType.OTHERS)
+ for (event in events) {
+ val radioButton = RadioButton(requireContext())
+ radioButton.text = event.value
+ radioButton.setRadioButtonColor()
+ generateQrCodeRadioGroup.addView(radioButton)
+ }
+ qrCodeGenerate.setOnClickListener {
+ //TODO: Set correct Venue Type
+ generateQrCode(titleEditText.text.toString(), VenueType.CAFETERIA)
+ }
- //TODO: Remove these hardcoded Events
- val events = arrayListOf(EventType.PRIVATE_EVENT, EventType.MEETING_ROOM, EventType.OFFICE, EventType.OTHERS)
- for (event in events) {
- val radioButton = RadioButton(requireContext())
- radioButton.text = event.value
- radioButton.setRadioButtonColor()
- binding.generateQrCodeRadioGroup.addView(radioButton)
}
-
- binding.qrCodeGenerate.setOnClickListener { generateQrCode() }
-
+ return binding.root
}
- private fun generateQrCode() {
- //TODO: Set correct Venue Type
- qrCodeViewModel.generateAndSaveQrCode(binding.titleEditText.text.toString(), VenueType.CAFETERIA)
+ private fun generateQrCode(title: String, venueType: VenueType) {
+ qrCodeViewModel.generateAndSaveQrCode(title, venueType)
cancel()
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index c84fc1371..33da8a013 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -10,6 +10,7 @@ import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
import androidx.lifecycle.*
+import ch.admin.bag.dp3t.BuildConfig
import ch.admin.bag.dp3t.checkin.models.GeneratedQrCodesWrapper
import ch.admin.bag.dp3t.checkin.models.SwissCovidLocationData
import ch.admin.bag.dp3t.checkin.models.VenueType
@@ -110,7 +111,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
val file = File(directory, QR_CODE_PDF_FILE_NAME)
val document = PdfDocument()
- val pageInfo = PdfDocument.PageInfo.Builder(1240, 1748, 1).create()
+ val pageInfo = PdfDocument.PageInfo.Builder(1240, 1748, 1).create() // A4 size
val page = document.startPage(pageInfo)
val canvas = page.canvas
@@ -137,7 +138,9 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
}
private fun generateQrCodeBitmap(venueInfo: VenueInfo) = viewModelScope.launch(Dispatchers.IO) {
- selectedQrCodeBitmap.postValue(QrCode.create(venueInfo.toQrCodeString()).renderToBitmap(QR_CODE_PIXEL_SIZE))
+ selectedQrCodeBitmap.postValue(
+ QrCode.create(venueInfo.toQrCodeString(BuildConfig.ENTRY_QR_CODE_PREFIX)).renderToBitmap(QR_CODE_PIXEL_SIZE)
+ )
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index 39efc2fae..46f10647b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -27,7 +27,6 @@ class QrCodeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
qrCodeViewModel.selectedQrCode?.let { venueInfo ->
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
index 27769c2f4..2b3479888 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/QrCodePayloadExtensions.kt
@@ -1,10 +1,10 @@
package ch.admin.bag.dp3t.checkin.utils
import ch.admin.bag.dp3t.checkin.models.QRCodePayload
-import org.crowdnotifier.android.sdk.CrowdNotifier
import org.crowdnotifier.android.sdk.model.VenueInfo
import org.crowdnotifier.android.sdk.utils.Base64Util
+import org.crowdnotifier.android.sdk.utils.QrUtils
fun QRCodePayload.toVenueInfo(): VenueInfo {
- return CrowdNotifier.getVenueInfo("?v=3#" + Base64Util.toBase64(this.toByteArray()), "")
+ return QrUtils.getVenueInfoFromQrCode(Base64Util.toBase64(this.toByteArray()))
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_events_overview.xml b/app/src/main/res/layout/fragment_events_overview.xml
index 8c4d95f06..875c50fd3 100644
--- a/app/src/main/res/layout/fragment_events_overview.xml
+++ b/app/src/main/res/layout/fragment_events_overview.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
app:navigationContentDescription="@string/android_accessibility_back_button"
app:navigationIcon="@drawable/ic_arrow_back"
- app:title="[TBT] Veranstaltungen" />
+ app:title="@string/events_title" />
+ android:text="@string/checkins_create_qr_code" />
@@ -84,6 +84,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginTop="@dimen/spacing_medium"
- android:text="@string/qr_code_erstellen_button" />
+ android:text="@string/checkins_create_qr_code" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
index 14c5bb822..3ebef1eae 100644
--- a/app/src/main/res/layout/fragment_qr_code.xml
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@color/white"
android:orientation="vertical"
android:padding="@dimen/spacing_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 dd34600f5..420b33600 100644
--- a/app/src/main/res/layout/item_generate_qr_code.xml
+++ b/app/src/main/res/layout/item_generate_qr_code.xml
@@ -11,6 +11,6 @@
style="@style/NextStep.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/qr_code_erstellen_button" />
+ android:text="@string/checkins_create_qr_code" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_qr_code_empty_list.xml b/app/src/main/res/layout/item_qr_code_empty_list.xml
index 05191d2ac..c7728bb3e 100644
--- a/app/src/main/res/layout/item_qr_code_empty_list.xml
+++ b/app/src/main/res/layout/item_qr_code_empty_list.xml
@@ -20,7 +20,7 @@
android:layout_gravity="center_horizontal"
android:gravity="center"
android:paddingVertical="20dp"
- android:text="@string/qr_code_empty_list"
+ android:text="[TBT] Keine QR-Codes von Veranstaltungen erstellt"
android:textStyle="bold" />
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ea93042fa..190de1942 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1648,10 +1648,5 @@ If so, contact the Coronavirus Infoline:"
"Snooze"
- [TBT] QR-Code erstellen
- [TBT] Keine QR-Codes von Veranstaltungen erstellt
- [TBT] QR-Code erstellen
- Kategorie
- Titel
- z.B. Raum- oder Eventname
+
\ No newline at end of file
From be7f26844e3ae9427e950c6421e7223f3c2354f9 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 4 May 2021 17:37:37 +0200
Subject: [PATCH 024/207] Implemented Check-In from QrCodeFragment
---
.../dp3t/checkin/CheckinOverviewFragment.kt | 2 +-
.../EventsOverviewFragment.kt | 5 +----
.../QrCodeAdapter.kt | 2 +-
.../QrCodeBaseViewHolder.kt | 2 +-
.../checkin/generateqrcode/QrCodeFragment.kt | 18 ++++++++++++++++++
.../{events => generateqrcode}/QrCodeItem.kt | 2 +-
6 files changed, 23 insertions(+), 8 deletions(-)
rename app/src/main/java/ch/admin/bag/dp3t/checkin/{events => generateqrcode}/EventsOverviewFragment.kt (90%)
rename app/src/main/java/ch/admin/bag/dp3t/checkin/{events => generateqrcode}/QrCodeAdapter.kt (98%)
rename app/src/main/java/ch/admin/bag/dp3t/checkin/{events => generateqrcode}/QrCodeBaseViewHolder.kt (80%)
rename app/src/main/java/ch/admin/bag/dp3t/checkin/{events => generateqrcode}/QrCodeItem.kt (56%)
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 f4439d1ac..ed52cd3d8 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
@@ -10,7 +10,7 @@ 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.checkinflow.QrCodeScannerFragment
-import ch.admin.bag.dp3t.checkin.events.EventsOverviewFragment
+import ch.admin.bag.dp3t.checkin.generateqrcode.EventsOverviewFragment
import ch.admin.bag.dp3t.databinding.FragmentCheckinOverviewBinding
import ch.admin.bag.dp3t.util.StringUtil
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
similarity index 90%
rename from app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
index a34518013..f20e6924b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.checkin.events
+package ch.admin.bag.dp3t.checkin.generateqrcode
import android.os.Bundle
import android.view.LayoutInflater
@@ -7,9 +7,6 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
-import ch.admin.bag.dp3t.checkin.generateqrcode.GenerateQrCodeFragment
-import ch.admin.bag.dp3t.checkin.generateqrcode.QRCodeViewModel
-import ch.admin.bag.dp3t.checkin.generateqrcode.QrCodeFragment
import ch.admin.bag.dp3t.databinding.FragmentEventsOverviewBinding
import org.crowdnotifier.android.sdk.model.VenueInfo
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
similarity index 98%
rename from app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
index 982a5731a..f91632cab 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.checkin.events
+package ch.admin.bag.dp3t.checkin.generateqrcode
import android.view.LayoutInflater
import android.view.ViewGroup
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeBaseViewHolder.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeBaseViewHolder.kt
similarity index 80%
rename from app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeBaseViewHolder.kt
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeBaseViewHolder.kt
index ebd765497..6b6dd7836 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeBaseViewHolder.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeBaseViewHolder.kt
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.checkin.events
+package ch.admin.bag.dp3t.checkin.generateqrcode
import android.view.View
import androidx.recyclerview.widget.RecyclerView
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index 46f10647b..5945818a1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -13,6 +13,11 @@ import androidx.core.content.FileProvider
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.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment
+import ch.admin.bag.dp3t.checkin.models.CheckInState
+import ch.admin.bag.dp3t.checkin.models.ReminderOption
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
import java.io.File
@@ -24,6 +29,7 @@ class QrCodeFragment : Fragment() {
}
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
+ private val crowdNotifierViewModel: CrowdNotifierViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -52,6 +58,11 @@ class QrCodeFragment : Fragment() {
qrCodeViewModel.deleteQrCode(venueInfo)
requireActivity().supportFragmentManager.popBackStack()
}
+ checkinButton.setOnClickListener {
+ crowdNotifierViewModel.checkInState =
+ CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), ReminderOption.OFF)
+ showCheckInFragment()
+ }
}
}.root
}
@@ -90,4 +101,11 @@ class QrCodeFragment : Fragment() {
}
}
+ private fun showCheckInFragment() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, CheckInFragment.newInstance())
+ .addToBackStack(CheckInFragment::class.java.canonicalName)
+ .commitAllowingStateLoss()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeItem.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
similarity index 56%
rename from app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeItem.kt
rename to app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
index 88199a9f6..df34967da 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/events/QrCodeItem.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
@@ -1,4 +1,4 @@
-package ch.admin.bag.dp3t.checkin.events
+package ch.admin.bag.dp3t.checkin.generateqrcode
class GenerateQrCodeItem {
From f3fe90ae0151a96a9b53aa3adc4dab29c31da4ef Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 4 May 2021 18:30:08 +0200
Subject: [PATCH 025/207] Fix DKA problem and disable check-in if already
checked-in
---
.../generateqrcode/EventsOverviewFragment.kt | 8 +-
.../generateqrcode/GenerateQrCodeFragment.kt | 6 +-
.../checkin/generateqrcode/QRCodeViewModel.kt | 28 ++-----
.../checkin/generateqrcode/QrCodeFragment.kt | 82 +++++++++----------
.../dp3t/checkin/generateqrcode/QrCodeItem.kt | 10 ---
.../checkin/generateqrcode/QrCodeItems.kt | 6 ++
.../main/res/drawable/bg_button_outlined.xml | 29 +++++--
.../drawable/outlined_button_text_color.xml | 8 ++
app/src/main/res/values/styles.xml | 3 +-
9 files changed, 92 insertions(+), 88 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItems.kt
create mode 100644 app/src/main/res/drawable/outlined_button_text_color.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
index f20e6924b..080c10ab7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
@@ -13,9 +13,7 @@ import org.crowdnotifier.android.sdk.model.VenueInfo
class EventsOverviewFragment : Fragment() {
companion object {
- fun newInstance(): EventsOverviewFragment {
- return EventsOverviewFragment()
- }
+ fun newInstance() = EventsOverviewFragment()
}
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
@@ -43,15 +41,13 @@ class EventsOverviewFragment : Fragment() {
qrCodeViewModel.generatedQrCodesLiveData.observe(viewLifecycleOwner, {
adapter.setItems(it)
})
-
}.root
}
private fun showQrCodeFragment(venueInfo: VenueInfo) {
- qrCodeViewModel.selectedQrCode = venueInfo
requireActivity().supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, QrCodeFragment.newInstance())
+ .replace(R.id.main_fragment_container, QrCodeFragment.newInstance(venueInfo))
.addToBackStack(QrCodeFragment::class.java.canonicalName)
.commit()
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
index 175cc9237..762d87df2 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
@@ -29,7 +29,7 @@ class GenerateQrCodeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentGenerateQrCodeBinding.inflate(layoutInflater).apply {
- generateQrCodeCancel.setOnClickListener { cancel() }
+ generateQrCodeCancel.setOnClickListener { popFragmentAndHideKeyboard() }
//TODO: Remove these hardcoded Events
val events = arrayListOf(EventType.PRIVATE_EVENT, EventType.MEETING_ROOM, EventType.OFFICE, EventType.OTHERS)
@@ -50,10 +50,10 @@ class GenerateQrCodeFragment : Fragment() {
private fun generateQrCode(title: String, venueType: VenueType) {
qrCodeViewModel.generateAndSaveQrCode(title, venueType)
- cancel()
+ popFragmentAndHideKeyboard()
}
- private fun cancel() {
+ private fun popFragmentAndHideKeyboard() {
requireActivity().supportFragmentManager.popBackStack()
val inputMethodManager = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.titleEditText.windowToken, 0)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index 33da8a013..ba1d62d32 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -44,7 +44,7 @@ private val Context.generatedQrCodesDataStore: DataStore().generatedQrCodesDataStore.data.buffer(1).catch { exception ->
if (exception is IOException) {
@@ -58,14 +58,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
wrapper.generatedQrCodesList.map { it.toVenueInfo() }
}.asLiveData()
- var selectedQrCode: VenueInfo? = null
- set(value) {
- selectedQrCodeBitmap.value = null
- value?.let { generateQrCodeBitmap(it) }
- field = value
- }
-
- val selectedQrCodeBitmap = MutableLiveData()
+ val selectedQrCodeBitmap = SingleLiveEvent()
val selectedQrCodePdf = SingleLiveEvent()
fun deleteQrCode(venueInfo: VenueInfo) = viewModelScope.launch {
@@ -105,7 +98,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
saveGeneratedQrCode(newWrapper)
}
- fun createQrCodePdf() = viewModelScope.launch(Dispatchers.IO) {
+ fun createQrCodePdf(bitmap: Bitmap) = viewModelScope.launch(Dispatchers.IO) {
val directory = File(getApplication().externalCacheDir, "pdfs").apply { if (!exists()) mkdirs() }
val file = File(directory, QR_CODE_PDF_FILE_NAME)
@@ -115,9 +108,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
val page = document.startPage(pageInfo)
val canvas = page.canvas
- selectedQrCodeBitmap.value?.let {
- canvas.drawBitmap(it, 120f, 120f, Paint())
- }
+ canvas.drawBitmap(bitmap, 120f, 120f, Paint())
document.finishPage(page)
FileOutputStream(file).use {
@@ -128,6 +119,11 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
selectedQrCodePdf.postValue(file)
}
+ fun generateQrCodeBitmap(venueInfo: VenueInfo) = viewModelScope.launch(Dispatchers.IO) {
+ selectedQrCodeBitmap.postValue(
+ QrCode.create(venueInfo.toQrCodeString(BuildConfig.ENTRY_QR_CODE_PREFIX)).renderToBitmap(QR_CODE_PIXEL_SIZE)
+ )
+ }
private suspend fun saveGeneratedQrCode(generatedQrCodesWrapper: GeneratedQrCodesWrapper) {
try {
@@ -137,10 +133,4 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
}
}
- private fun generateQrCodeBitmap(venueInfo: VenueInfo) = viewModelScope.launch(Dispatchers.IO) {
- selectedQrCodeBitmap.postValue(
- QrCode.create(venueInfo.toQrCodeString(BuildConfig.ENTRY_QR_CODE_PREFIX)).renderToBitmap(QR_CODE_PIXEL_SIZE)
- )
- }
-
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index 5945818a1..348a37bd1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -10,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.FileProvider
+import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@@ -17,15 +18,23 @@ import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment
import ch.admin.bag.dp3t.checkin.models.CheckInState
+import ch.admin.bag.dp3t.checkin.models.QRCodePayload
import ch.admin.bag.dp3t.checkin.models.ReminderOption
+import ch.admin.bag.dp3t.checkin.utils.toQrCodePayload
+import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
+import com.google.protobuf.ByteString
+import org.crowdnotifier.android.sdk.model.VenueInfo
import java.io.File
+private const val KEY_VENUE_INFO = "KEY_VENUE_INFO"
class QrCodeFragment : Fragment() {
companion object {
- fun newInstance() = QrCodeFragment()
+ fun newInstance(venueInfo: VenueInfo) = QrCodeFragment().apply {
+ arguments = bundleOf(KEY_VENUE_INFO to venueInfo.toQrCodePayload().toByteString())
+ }
}
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
@@ -35,48 +44,39 @@ class QrCodeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- qrCodeViewModel.selectedQrCode?.let { venueInfo ->
- titleTextview.text = venueInfo.title
- subtitleTextview.text = "TODO"
- qrCodeViewModel.selectedQrCodeBitmap.observe(viewLifecycleOwner) {
- if (it == null) {
- qrCodeImageview.visibility = View.INVISIBLE
- qrCodeLoadingProgressbar.isVisible = true
- shareButton.isEnabled = false
- printPdfButton.isEnabled = false
- } else {
- qrCodeImageview.visibility = View.VISIBLE
- qrCodeLoadingProgressbar.isVisible = false
- qrCodeImageview.setImageBitmap(it)
- shareButton.isEnabled = true
- printPdfButton.isEnabled = true
- }
- }
- shareButton.setOnClickListener { generatePdf(isPrint = false) }
- printPdfButton.setOnClickListener { generatePdf(isPrint = true) }
- deleteButton.setOnClickListener {
- qrCodeViewModel.deleteQrCode(venueInfo)
- requireActivity().supportFragmentManager.popBackStack()
- }
- checkinButton.setOnClickListener {
- crowdNotifierViewModel.checkInState =
- CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), ReminderOption.OFF)
- showCheckInFragment()
- }
+ val venueInfo = QRCodePayload.parseFrom(arguments?.get(KEY_VENUE_INFO) as ByteString).toVenueInfo()
+ titleTextview.text = venueInfo.title
+ subtitleTextview.text = "TODO"
+ qrCodeImageview.visibility = View.INVISIBLE
+ qrCodeLoadingProgressbar.isVisible = true
+ shareButton.isEnabled = false
+ printPdfButton.isEnabled = false
+ qrCodeViewModel.selectedQrCodeBitmap.observe(viewLifecycleOwner) {
+ qrCodeImageview.visibility = View.VISIBLE
+ qrCodeLoadingProgressbar.isVisible = false
+ qrCodeImageview.setImageBitmap(it)
+ qrCodeViewModel.createQrCodePdf(it)
}
- }.root
- }
-
- private fun generatePdf(isPrint: Boolean) {
- val pdf = qrCodeViewModel.selectedQrCodePdf.value
- if (pdf == null) {
- qrCodeViewModel.selectedQrCodePdf.observe(viewLifecycleOwner) {
- if (isPrint) printPdf(it) else sharePdf(it)
+ qrCodeViewModel.selectedQrCodePdf.observe(viewLifecycleOwner) { pdfFile ->
+ shareButton.isEnabled = true
+ printPdfButton.isEnabled = true
+ shareButton.setOnClickListener { sharePdf(pdfFile) }
+ printPdfButton.setOnClickListener { printPdf(pdfFile) }
}
- qrCodeViewModel.createQrCodePdf()
- } else {
- if (isPrint) printPdf(pdf) else sharePdf(pdf)
- }
+ qrCodeViewModel.generateQrCodeBitmap(venueInfo)
+ deleteButton.setOnClickListener {
+ qrCodeViewModel.deleteQrCode(venueInfo)
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner) { isCheckedIn ->
+ checkinButton.isEnabled = !isCheckedIn
+ }
+ checkinButton.setOnClickListener {
+ crowdNotifierViewModel.checkInState =
+ CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), ReminderOption.OFF)
+ showCheckInFragment()
+ }
+ }.root
}
private fun printPdf(file: File) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
deleted file mode 100644
index df34967da..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItem.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package ch.admin.bag.dp3t.checkin.generateqrcode
-
-class GenerateQrCodeItem {
-
-}
-
-class QrCodeEmptyListItem {
-
-}
-
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItems.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItems.kt
new file mode 100644
index 000000000..4e2415339
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeItems.kt
@@ -0,0 +1,6 @@
+package ch.admin.bag.dp3t.checkin.generateqrcode
+
+class GenerateQrCodeItem
+
+class QrCodeEmptyListItem
+
diff --git a/app/src/main/res/drawable/bg_button_outlined.xml b/app/src/main/res/drawable/bg_button_outlined.xml
index b22125795..a45c24395 100644
--- a/app/src/main/res/drawable/bg_button_outlined.xml
+++ b/app/src/main/res/drawable/bg_button_outlined.xml
@@ -1,5 +1,4 @@
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/outlined_button_text_color.xml b/app/src/main/res/drawable/outlined_button_text_color.xml
new file mode 100644
index 000000000..7be84b0a8
--- /dev/null
+++ b/app/src/main/res/drawable/outlined_button_text_color.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4fd8a10bc..c3e132417 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -247,7 +247,8 @@
From 7102c4e8335fbe8a24f93f4626570118952fe06a Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 5 May 2021 10:46:44 +0200
Subject: [PATCH 026/207] PP-973: Implemented Info Tab
---
.../ch/admin/bag/dp3t/TabbarHostFragment.java | 7 ++-
.../dp3t/checkin/CheckinOverviewFragment.kt | 11 ----
.../ch/admin/bag/dp3t/home/HomeFragment.java | 56 -----------------
.../admin/bag/dp3t/infotab/InfoTabFragment.kt | 58 +++++++++++++++++
.../admin/bag/dp3t/util/FragmentExtensions.kt | 12 ++++
.../res/layout/fragment_checkin_overview.xml | 27 --------
app/src/main/res/layout/fragment_home.xml | 20 ------
app/src/main/res/layout/fragment_info_tab.xml | 62 +++++++++++++++++++
app/src/main/res/menu/menu_bottom_nav_bar.xml | 4 ++
9 files changed, 142 insertions(+), 115 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/FragmentExtensions.kt
create mode 100644 app/src/main/res/layout/fragment_info_tab.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/TabbarHostFragment.java b/app/src/main/java/ch/admin/bag/dp3t/TabbarHostFragment.java
index 34938a7ee..b8cdc100e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/TabbarHostFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/TabbarHostFragment.java
@@ -25,6 +25,7 @@
import ch.admin.bag.dp3t.debug.DebugFragment;
import ch.admin.bag.dp3t.home.HomeFragment;
import ch.admin.bag.dp3t.html.HtmlFragment;
+import ch.admin.bag.dp3t.infotab.InfoTabFragment;
import ch.admin.bag.dp3t.stats.StatsFragment;
import ch.admin.bag.dp3t.stats.StatsViewModel;
import ch.admin.bag.dp3t.util.AssetUtil;
@@ -112,7 +113,6 @@ private void setupBottomNavigationView() {
lastSelectedTab = item.getItemId();
lastTabSwitch = System.currentTimeMillis();
- // Use a switch anyway, there may be more tabs in the future
switch (item.getItemId()) {
case R.id.bottom_nav_stats:
getChildFragmentManager().beginTransaction()
@@ -121,6 +121,11 @@ private void setupBottomNavigationView() {
statsViewModel.loadStats();
break;
+ case R.id.bottom_nav_info:
+ getChildFragmentManager().beginTransaction()
+ .replace(R.id.tabs_fragment_container, InfoTabFragment.newInstance())
+ .commit();
+ break;
default:
getChildFragmentManager().beginTransaction()
.replace(R.id.tabs_fragment_container, HomeFragment.newInstance())
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 ed52cd3d8..8a24e79dd 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
@@ -10,7 +10,6 @@ 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.checkinflow.QrCodeScannerFragment
-import ch.admin.bag.dp3t.checkin.generateqrcode.EventsOverviewFragment
import ch.admin.bag.dp3t.databinding.FragmentCheckinOverviewBinding
import ch.admin.bag.dp3t.util.StringUtil
@@ -29,8 +28,6 @@ class CheckinOverviewFragment : Fragment() {
return FragmentCheckinOverviewBinding.inflate(layoutInflater).apply {
checkinOverviewToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- qrCodeGenerate.setOnClickListener { showEventsOverviewFragment() }
-
checkinOverviewHistory.setOnClickListener { v: View? -> }
crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner, { isCheckedIn ->
@@ -50,14 +47,6 @@ class CheckinOverviewFragment : Fragment() {
}.root
}
- private fun showEventsOverviewFragment() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, EventsOverviewFragment.newInstance())
- .addToBackStack(EventsOverviewFragment::class.java.canonicalName)
- .commit()
- }
-
private fun showCheckOutFragment() {
requireActivity().supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
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 e74043f37..7bec4252e 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
@@ -29,13 +29,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
-import androidx.constraintlayout.helper.widget.Flow;
import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
-import java.util.List;
import java.util.concurrent.TimeUnit;
import org.dpppt.android.sdk.TracingStatus;
@@ -57,13 +55,9 @@
import ch.admin.bag.dp3t.networking.models.InfoBoxModelCollection;
import ch.admin.bag.dp3t.reports.ReportsFragment;
import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.travel.TravelFragment;
-import ch.admin.bag.dp3t.travel.TravelUtils;
import ch.admin.bag.dp3t.util.*;
import ch.admin.bag.dp3t.viewmodel.TracingViewModel;
import ch.admin.bag.dp3t.whattodo.WtdInfolineAccessabilityDialogFragment;
-import ch.admin.bag.dp3t.whattodo.WtdPositiveTestFragment;
-import ch.admin.bag.dp3t.whattodo.WtdSymptomsFragment;
import static android.view.View.VISIBLE;
@@ -81,12 +75,7 @@ public class HomeFragment extends Fragment {
private View reportStatusBubble;
private View reportStatusView;
private View reportErrorView;
- private View travelCard;
private View checkinCard;
- private View cardSymptomsFrame;
- private View cardTestFrame;
- private View cardSymptoms;
- private View cardTest;
private View loadingView;
private SecureStorage secureStorage;
@@ -122,14 +111,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
reportStatusBubble = view.findViewById(R.id.report_status_bubble);
reportStatusView = reportStatusBubble.findViewById(R.id.report_status);
reportErrorView = reportStatusBubble.findViewById(R.id.report_errors);
- travelCard = view.findViewById(R.id.card_travel);
checkinCard = view.findViewById(R.id.card_checkin);
headerView = view.findViewById(R.id.home_header_view);
scrollView = view.findViewById(R.id.home_scroll_view);
- cardSymptoms = view.findViewById(R.id.card_what_to_do_symptoms);
- cardSymptomsFrame = view.findViewById(R.id.frame_card_symptoms);
- cardTest = view.findViewById(R.id.card_what_to_do_test);
- cardTestFrame = view.findViewById(R.id.frame_card_test);
loadingView = view.findViewById(R.id.loading_view);
setupHeader();
@@ -137,8 +121,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
setupTracingView();
setupNotification();
setupCheckinCard();
- setupTravelCard();
- setupWhatToDo();
setupNonProductionHint();
setupScrollBehavior();
@@ -246,14 +228,10 @@ private void setupTracingView() {
tracingViewModel.getAppStatusLiveData().observe(getViewLifecycleOwner(), tracingStatusInterface -> {
if (tracingStatusInterface.isReportedAsInfected()) {
- cardSymptomsFrame.setVisibility(View.GONE);
- cardTestFrame.setVisibility(View.GONE);
tracingCard.findViewById(R.id.contacs_chevron).setVisibility(View.GONE);
tracingCard.setOnClickListener(null);
tracingCard.setForeground(null);
} else {
- cardSymptomsFrame.setVisibility(VISIBLE);
- cardTestFrame.setVisibility(VISIBLE);
tracingCard.findViewById(R.id.contacs_chevron).setVisibility(VISIBLE);
tracingCard.setOnClickListener(v -> showContactsFragment());
}
@@ -419,40 +397,6 @@ private void showCheckinOverviewFragment() {
.commit();
}
- private void setupTravelCard() {
- travelCard.setOnClickListener(
- v -> requireActivity().getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, TravelFragment.newInstance())
- .addToBackStack(TravelFragment.class.getCanonicalName())
- .commit()
- );
-
- List countries = secureStorage.getInteropCountries();
- if (!countries.isEmpty()) {
- travelCard.setVisibility(VISIBLE);
- Flow flowConstraint = travelCard.findViewById(R.id.travel_flags_flow);
- TravelUtils.inflateFlagFlow(flowConstraint, countries);
- } else {
- travelCard.setVisibility(View.GONE);
- }
- }
-
- private void setupWhatToDo() {
- cardSymptoms.setOnClickListener(
- v -> requireActivity().getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, WtdSymptomsFragment.newInstance())
- .addToBackStack(WtdSymptomsFragment.class.getCanonicalName())
- .commit());
- cardTest.setOnClickListener(
- v -> requireActivity().getSupportFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, WtdPositiveTestFragment.newInstance())
- .addToBackStack(WtdPositiveTestFragment.class.getCanonicalName())
- .commit());
- }
-
private void setupNonProductionHint() {
View nonProduction = requireView().findViewById(R.id.non_production_message);
if (BuildConfig.IS_FLAVOR_PROD || BuildConfig.IS_FLAVOR_ABNAHME) {
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
new file mode 100644
index 000000000..949a7d48d
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/infotab/InfoTabFragment.kt
@@ -0,0 +1,58 @@
+package ch.admin.bag.dp3t.infotab
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import ch.admin.bag.dp3t.checkin.generateqrcode.EventsOverviewFragment
+import ch.admin.bag.dp3t.databinding.FragmentInfoTabBinding
+import ch.admin.bag.dp3t.storage.SecureStorage
+import ch.admin.bag.dp3t.travel.TravelFragment
+import ch.admin.bag.dp3t.travel.TravelUtils
+import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.viewmodel.TracingViewModel
+import ch.admin.bag.dp3t.whattodo.WtdPositiveTestFragment
+import ch.admin.bag.dp3t.whattodo.WtdSymptomsFragment
+
+class InfoTabFragment : Fragment() {
+
+
+ companion object {
+ @JvmStatic
+ fun newInstance() = InfoTabFragment()
+ }
+
+ private lateinit var binding: FragmentInfoTabBinding
+ private val secureStorage by lazy { SecureStorage.getInstance(requireContext()) }
+ private val tracingViewModel: TracingViewModel by activityViewModels()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ binding = FragmentInfoTabBinding.inflate(inflater).apply {
+ frameCardSymptoms.root.setOnClickListener { showFragment(WtdSymptomsFragment.newInstance()) }
+ frameCardTest.root.setOnClickListener { showFragment(WtdPositiveTestFragment.newInstance()) }
+ tracingViewModel.appStatusLiveData.observe(viewLifecycleOwner) { tracingStatusInterface ->
+ frameCardSymptoms.root.isVisible = !tracingStatusInterface.isReportedAsInfected
+ frameCardTest.root.isVisible = !tracingStatusInterface.isReportedAsInfected
+ }
+ qrCodeGenerate.setOnClickListener { showFragment(EventsOverviewFragment.newInstance()) }
+ }
+ setupTravelCard()
+ return binding.root
+ }
+
+
+ private fun setupTravelCard() {
+ binding.cardTravel.apply {
+ cardTravel.setOnClickListener { showFragment(TravelFragment.newInstance()) }
+ val countries: List = secureStorage.interopCountries
+ cardTravel.isVisible = countries.isNotEmpty()
+ if (countries.isNotEmpty()) {
+ TravelUtils.inflateFlagFlow(travelFlagsFlow, countries)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/FragmentExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/util/FragmentExtensions.kt
new file mode 100644
index 000000000..fda1fe032
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/FragmentExtensions.kt
@@ -0,0 +1,12 @@
+package ch.admin.bag.dp3t.util
+
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.R
+
+fun Fragment.showFragment(fragment: Fragment){
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, fragment)
+ .addToBackStack(fragment::class.java.canonicalName)
+ .commit()
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
index 8635f0938..09058451b 100644
--- a/app/src/main/res/layout/fragment_checkin_overview.xml
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -172,33 +172,6 @@
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 3fd3debde..3698f92af 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -68,26 +68,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_medium_large" />
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_bottom_nav_bar.xml b/app/src/main/res/menu/menu_bottom_nav_bar.xml
index f3adadba1..6b62f7bd6 100644
--- a/app/src/main/res/menu/menu_bottom_nav_bar.xml
+++ b/app/src/main/res/menu/menu_bottom_nav_bar.xml
@@ -12,6 +12,10 @@
android:id="@+id/bottom_nav_home"
android:icon="@drawable/ic_begegnungen"
android:title="@string/bottom_nav_tab_home" />
+
Date: Tue, 4 May 2021 15:10:57 +0200
Subject: [PATCH 027/207] added card and biometrics for diary screen
---
app/build.gradle | 2 +
.../dp3t/checkin/CheckinOverviewFragment.kt | 36 ++++-
.../bag/dp3t/checkin/diary/DiaryFragment.kt | 21 +++
app/src/main/res/drawable/illu_diary.xml | 124 ++++++++++++++++++
.../res/layout/fragment_checkin_overview.xml | 59 +++++++--
5 files changed, 232 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt
create mode 100644 app/src/main/res/drawable/illu_diary.xml
diff --git a/app/build.gradle b/app/build.gradle
index 700cf2f49..3d9d1e094 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,6 +239,8 @@ dependencies {
//DO NOT UPDATE ZXING TO 3.4.0! (3.4.0 is not compatible with API Level 23 or lower)
implementation "com.google.zxing:core:3.3.0"
+ implementation 'androidx.biometric:biometric:1.1.0'
+
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test:core:1.3.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 8a24e79dd..0e544a345 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
@@ -4,12 +4,16 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.biometric.BiometricManager
+import androidx.biometric.BiometricPrompt
+import androidx.core.content.ContextCompat
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.checkinflow.QrCodeScannerFragment
+import ch.admin.bag.dp3t.checkin.diary.DiaryFragment
import ch.admin.bag.dp3t.databinding.FragmentCheckinOverviewBinding
import ch.admin.bag.dp3t.util.StringUtil
@@ -28,7 +32,7 @@ class CheckinOverviewFragment : Fragment() {
return FragmentCheckinOverviewBinding.inflate(layoutInflater).apply {
checkinOverviewToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
- checkinOverviewHistory.setOnClickListener { v: View? -> }
+ checkinOverviewDiary.setOnClickListener { authenticateAndShowDiary() }
crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner, { isCheckedIn ->
checkoutView.isVisible = isCheckedIn
@@ -62,4 +66,34 @@ class CheckinOverviewFragment : Fragment() {
.addToBackStack(QrCodeScannerFragment::class.java.canonicalName)
.commit()
}
+
+ private fun authenticateAndShowDiary() {
+ val executor = ContextCompat.getMainExecutor(requireContext())
+ val biometricPrompt = BiometricPrompt(requireActivity(), executor, object : BiometricPrompt.AuthenticationCallback() {
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ showDiaryFragment()
+ }
+ })
+
+ if (BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
+ ) {
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
+ .setTitle(getString(R.string.authenticate_for_diary))
+ .setAllowedAuthenticators(
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK
+ )
+ .build()
+ biometricPrompt.authenticate(promptInfo)
+ } else {
+ showDiaryFragment()
+ }
+ }
+
+ private fun showDiaryFragment() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, DiaryFragment.newInstance())
+ .addToBackStack(DiaryFragment::class.java.canonicalName)
+ .commit()
+ }
}
\ No newline at end of file
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
new file mode 100644
index 000000000..b28ae960a
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt
@@ -0,0 +1,21 @@
+package ch.admin.bag.dp3t.checkin.diary
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+
+class DiaryFragment : Fragment() {
+
+ companion object {
+ fun newInstance(): DiaryFragment {
+ return DiaryFragment()
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ TODO()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/illu_diary.xml b/app/src/main/res/drawable/illu_diary.xml
new file mode 100644
index 000000000..f7ca670e0
--- /dev/null
+++ b/app/src/main/res/drawable/illu_diary.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
index 09058451b..e8a40485b 100644
--- a/app/src/main/res/layout/fragment_checkin_overview.xml
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -142,31 +142,72 @@
+
+ android:layout_marginBottom="@dimen/spacing_medium_large"
+ android:foreground="?selectableItemBackground">
-
+
+
+
+
+
+
+
+
+ android:layout_margin="@dimen/spacing_medium"
+ android:orientation="horizontal">
+
+
+
+
+
+
From d25bec1b23518c9e5c3ca33add233e065e4c15a3 Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Thu, 6 May 2021 14:33:27 +0200
Subject: [PATCH 028/207] diary list screen
---
.../dp3t/checkin/CheckinOverviewFragment.kt | 18 +--
.../bag/dp3t/checkin/diary/DiaryFragment.kt | 69 ++++++++-
.../diary/DiaryRecyclerViewAdapter.java | 132 ++++++++++++++++++
.../checkin/diary/items/ItemVenueVisit.java | 38 +++++
.../diary/items/ItemVenueVisitDayHeader.java | 20 +++
.../diary/items/VenueVisitRecyclerItem.java | 11 ++
.../bag/dp3t/checkin/utils/DateTimeUtils.java | 52 +++++++
.../dp3t/checkin/utils/VenueInfoExtensions.kt | 2 +-
.../res/drawable-nodpi/hidden_event_blur.png | Bin 0 -> 956 bytes
.../res/layout/fragment_checkin_diary.xml | 74 ++++++++++
.../res/layout/item_checkin_venue_visit.xml | 104 ++++++++++++++
.../item_checkin_venue_visits_day_header.xml | 16 +++
app/src/main/res/values/colors.xml | 2 +-
.../main/res/values/crowdnotifier_styles.xml | 11 ++
app/src/main/res/values/dimens.xml | 2 +
15 files changed, 538 insertions(+), 13 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisit.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisitDayHeader.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/VenueVisitRecyclerItem.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/utils/DateTimeUtils.java
create mode 100644 app/src/main/res/drawable-nodpi/hidden_event_blur.png
create mode 100644 app/src/main/res/layout/fragment_checkin_diary.xml
create mode 100644 app/src/main/res/layout/item_checkin_venue_visit.xml
create mode 100644 app/src/main/res/layout/item_checkin_venue_visits_day_header.xml
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 0e544a345..8678efffb 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
@@ -68,15 +68,15 @@ class CheckinOverviewFragment : Fragment() {
}
private fun authenticateAndShowDiary() {
- val executor = ContextCompat.getMainExecutor(requireContext())
- val biometricPrompt = BiometricPrompt(requireActivity(), executor, object : BiometricPrompt.AuthenticationCallback() {
- override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
- showDiaryFragment()
- }
- })
-
- if (BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
- ) {
+ val biometricState = BiometricManager.from(requireContext())
+ .canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ if (biometricState == BiometricManager.BIOMETRIC_SUCCESS) {
+ val executor = ContextCompat.getMainExecutor(requireContext())
+ val biometricPrompt = BiometricPrompt(requireActivity(), executor, object : BiometricPrompt.AuthenticationCallback() {
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ showDiaryFragment()
+ }
+ })
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.authenticate_for_diary))
.setAllowedAuthenticators(
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 b28ae960a..2834ac460 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
@@ -4,18 +4,83 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+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.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisit
+import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitDayHeader
+import ch.admin.bag.dp3t.checkin.diary.items.VenueVisitRecyclerItem
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
+import ch.admin.bag.dp3t.checkin.utils.DateTimeUtils
+import ch.admin.bag.dp3t.databinding.FragmentCheckinDiaryBinding
+import ch.admin.bag.dp3t.reports.ReportsFragment
+import org.crowdnotifier.android.sdk.model.ExposureEvent
+import java.util.*
class DiaryFragment : Fragment() {
+ private val crowdNotifierViewModel: CrowdNotifierViewModel by activityViewModels()
+
companion object {
fun newInstance(): DiaryFragment {
return DiaryFragment()
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- TODO()
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return FragmentCheckinDiaryBinding.inflate(inflater, container, false).apply {
+ checkinDiaryToolbar.setNavigationOnClickListener {
+ parentFragmentManager.popBackStack()
+ }
+
+ val recyclerAdapter = DiaryRecyclerViewAdapter()
+ checkinDiaryRecyclerView.adapter = recyclerAdapter
+
+ crowdNotifierViewModel.exposures.observe(viewLifecycleOwner) { exposures ->
+ val items: ArrayList = ArrayList()
+ val diaryEntries: List = DiaryStorage.getInstance(context).entries.sortedByDescending {
+ it.arrivalTime
+ }
+ val isEmpty = diaryEntries.isEmpty()
+ checkinDiaryEmptyView.isVisible = isEmpty
+ var daysAgoString = ""
+ for (diaryEntry in diaryEntries) {
+ val newDaysAgoString: String = DateTimeUtils.getDaysAgoString(diaryEntry.getArrivalTime(), context)
+ if (newDaysAgoString != daysAgoString) {
+ daysAgoString = newDaysAgoString
+ items.add(ItemVenueVisitDayHeader(daysAgoString))
+ }
+ items.add(ItemVenueVisit(getExposureWithId(exposures, diaryEntry.id), diaryEntry) { v ->
+ onDiaryEntryClicked(diaryEntry, getExposureWithId(exposures, diaryEntry.id))
+ })
+ }
+ recyclerAdapter.setData(items)
+ }
+ }.root
+ }
+
+
+ private fun getExposureWithId(exposures: List, id: Long): ExposureEvent? {
+ return exposures.firstOrNull { it.id == id }
+ }
+
+ private fun onDiaryEntryClicked(diaryEntry: DiaryEntry, exposureEvent: ExposureEvent?) {
+ if (exposureEvent != null) {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, ReportsFragment.newInstance())
+ .addToBackStack(ReportsFragment::class.java.canonicalName)
+ .commit()
+ } else {
+ /*requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .replace(R.id.main_fragment_container, EditDiaryEntryFragment.newInstance(true, diaryEntry.id))
+ .addToBackStack(EditDiaryEntryFragment::class.java.canonicalName)
+ .commit()*/
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
new file mode 100644
index 000000000..3a8209f6f
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
@@ -0,0 +1,132 @@
+package ch.admin.bag.dp3t.checkin.diary;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+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.diary.items.ItemVenueVisit;
+import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitDayHeader;
+import ch.admin.bag.dp3t.checkin.diary.items.VenueVisitRecyclerItem;
+import ch.admin.bag.dp3t.checkin.utils.DateTimeUtils;
+import ch.admin.bag.dp3t.checkin.utils.VenueInfoExtensionsKt;
+
+public class DiaryRecyclerViewAdapter extends RecyclerView.Adapter {
+
+ private final List items = new ArrayList<>();
+
+ @Override
+ public int getItemViewType(int position) { return items.get(position).getViewType().ordinal(); }
+
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ VenueVisitRecyclerItem.ViewType type = VenueVisitRecyclerItem.ViewType.values()[viewType];
+
+ switch (type) {
+ case DAY_HEADER:
+ return new DayHeaderViewHolder(
+ LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkin_venue_visits_day_header, parent, false));
+ case VENUE:
+ return new VenueVisitViewHolder(
+ LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkin_venue_visit, parent, false));
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ VenueVisitRecyclerItem item = items.get(position);
+
+ switch (item.getViewType()) {
+ case DAY_HEADER:
+ ((DayHeaderViewHolder) holder).bind((ItemVenueVisitDayHeader) item);
+ break;
+ case VENUE:
+ ((VenueVisitViewHolder) holder).bind((ItemVenueVisit) item);
+ break;
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ public void setData(List items) {
+ this.items.clear();
+ this.items.addAll(items);
+ notifyDataSetChanged();
+ }
+
+
+ public static class DayHeaderViewHolder extends RecyclerView.ViewHolder {
+
+ private final TextView dayLabel;
+
+ public DayHeaderViewHolder(View itemView) {
+ super(itemView);
+ dayLabel = itemView.findViewById(R.id.item_diary_day_header_text_view);
+ }
+
+ public void bind(ItemVenueVisitDayHeader item) {
+ dayLabel.setText(item.getDayLabel());
+ }
+
+ }
+
+
+ public static class VenueVisitViewHolder extends RecyclerView.ViewHolder {
+
+ private final TextView timeTextView;
+ private final TextView nameTextView;
+ private final TextView locationTextView;
+ private final ImageView statusIcon;
+ private final View infoBox;
+ private final TextView infoBoxText;
+
+ public VenueVisitViewHolder(View itemView) {
+ super(itemView);
+ this.timeTextView = itemView.findViewById(R.id.item_diary_entry_time);
+ this.nameTextView = itemView.findViewById(R.id.item_diary_entry_name);
+ this.locationTextView = itemView.findViewById(R.id.item_diary_entry_location);
+ this.statusIcon = itemView.findViewById(R.id.item_diary_entry_status_icon);
+ this.infoBox = itemView.findViewById(R.id.item_diary_entry_infobox);
+ this.infoBoxText = itemView.findViewById(R.id.item_diary_entry_infobox_text);
+ }
+
+ public void bind(ItemVenueVisit item) {
+ VenueInfo venueInfo = item.getDiaryEntry().getVenueInfo();
+ nameTextView.setText(venueInfo.getTitle());
+ locationTextView.setText(VenueInfoExtensionsKt.getSubtitle(venueInfo));
+ String start = DateTimeUtils.getHourMinuteTimeString(item.getDiaryEntry().getArrivalTime(), ":");
+ String end = DateTimeUtils.getHourMinuteTimeString(item.getDiaryEntry().getDepartureTime(), ":");
+ timeTextView.setText(start + " — " + end);
+ if (item.getExposure() == null) {
+ infoBox.setVisibility(View.GONE);
+ statusIcon.setVisibility(View.GONE);
+ } else {
+ if (item.getExposure().getMessage() != null && !item.getExposure().getMessage().isEmpty()) {
+ infoBox.setVisibility(View.VISIBLE);
+ infoBoxText.setText(item.getExposure().getMessage());
+ } else {
+ infoBox.setVisibility(View.GONE);
+ }
+ statusIcon.setVisibility(View.VISIBLE);
+ }
+ itemView.setOnClickListener(item.getOnClickListener());
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisit.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisit.java
new file mode 100644
index 000000000..d5cf871ac
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisit.java
@@ -0,0 +1,38 @@
+package ch.admin.bag.dp3t.checkin.diary.items;
+
+import android.view.View;
+
+import org.crowdnotifier.android.sdk.model.ExposureEvent;
+
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
+
+public class ItemVenueVisit extends VenueVisitRecyclerItem {
+
+ private final ExposureEvent exposure;
+ private final DiaryEntry diaryEntry;
+ private final View.OnClickListener onClickListener;
+
+ public ItemVenueVisit(ExposureEvent exposure, DiaryEntry diaryEntry, View.OnClickListener onClickListener) {
+ this.exposure = exposure;
+ this.diaryEntry = diaryEntry;
+ this.onClickListener = onClickListener;
+ }
+
+ public ExposureEvent getExposure() {
+ return exposure;
+ }
+
+ public DiaryEntry getDiaryEntry() {
+ return diaryEntry;
+ }
+
+ public View.OnClickListener getOnClickListener() {
+ return onClickListener;
+ }
+
+ @Override
+ public ViewType getViewType() {
+ return ViewType.VENUE;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisitDayHeader.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisitDayHeader.java
new file mode 100644
index 000000000..2c91b13a0
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/ItemVenueVisitDayHeader.java
@@ -0,0 +1,20 @@
+package ch.admin.bag.dp3t.checkin.diary.items;
+
+public class ItemVenueVisitDayHeader extends VenueVisitRecyclerItem {
+
+ private final String dayLabel;
+
+ public ItemVenueVisitDayHeader(String dayLabel) {
+ this.dayLabel = dayLabel;
+ }
+
+ public String getDayLabel() {
+ return dayLabel;
+ }
+
+ @Override
+ public ViewType getViewType() {
+ return ViewType.DAY_HEADER;
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/VenueVisitRecyclerItem.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/VenueVisitRecyclerItem.java
new file mode 100644
index 000000000..b648735b3
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/items/VenueVisitRecyclerItem.java
@@ -0,0 +1,11 @@
+package ch.admin.bag.dp3t.checkin.diary.items;
+
+public abstract class VenueVisitRecyclerItem {
+
+ public enum ViewType {
+ VENUE, DAY_HEADER
+ }
+
+ public abstract ViewType getViewType();
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/DateTimeUtils.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/DateTimeUtils.java
new file mode 100644
index 000000000..b3e02390d
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/DateTimeUtils.java
@@ -0,0 +1,52 @@
+package ch.admin.bag.dp3t.checkin.utils;
+
+import android.content.Context;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import ch.admin.bag.dp3t.R;
+
+public class DateTimeUtils {
+
+ public static String getHourMinuteTimeString(long timeStamp, String delimiter) {
+ Calendar time = Calendar.getInstance();
+ time.setTimeInMillis(timeStamp);
+ return prependZero(time.get(Calendar.HOUR_OF_DAY)) + delimiter + prependZero(time.get(Calendar.MINUTE));
+ }
+
+ public static String getDaysAgoString(long timeStamp, Context context) {
+ final long diff = getStartOfDay().getTime() - timeStamp;
+ if (diff < 0) {
+ return context.getResources().getString(R.string.date_today);
+ } else {
+ long daysAgo = TimeUnit.MILLISECONDS.toDays(diff) + 1;
+ if (daysAgo == 1) {
+ return context.getResources().getString(R.string.date_one_day_ago);
+ } else {
+ return context.getResources().getString(R.string.date_days_ago)
+ .replace("{COUNT}", String.valueOf(daysAgo));
+ }
+ }
+ }
+
+ private static String prependZero(int timeUnit) {
+ if (timeUnit < 10) {
+ return "0" + timeUnit;
+ } else {
+ return String.valueOf(timeUnit);
+ }
+ }
+
+ private static Date getStartOfDay() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(new Date());
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ return calendar.getTime();
+ }
+
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
index bed63156b..8d6fa1a1e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/VenueInfoExtensions.kt
@@ -19,7 +19,7 @@ fun VenueInfo.getSwissCovidLocationData(): SwissCovidLocationData {
fun VenueInfo.getSubtitle(): String {
//TODO: This is to be defined what the subtitle will be for SwissCovid
- return ""
+ return "*subtitle*"
}
fun VenueInfo.toQrCodePayload(): QRCodePayload {
diff --git a/app/src/main/res/drawable-nodpi/hidden_event_blur.png b/app/src/main/res/drawable-nodpi/hidden_event_blur.png
new file mode 100644
index 0000000000000000000000000000000000000000..46a85d1df51d9d6c68c9e8729a171cc9be1d3291
GIT binary patch
literal 956
zcmV;t14I0YP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D15rstK~#8N?VQ_=
z;xG_I+1vkr@Lpz9h!YjPb_QlhoK5OTC3kEOFU^r5MDykHIX?UM;W^%JW7rwbh!AP^
zMWoshkH}H?_;uvk2A8G^6rpy&dm^-?(MW{v39koHG!o$gc^w9@B!RCX&R45FVLeDc6
zE)gPqpMprp#JXS%uf+vHCM5E(E*Qgxv;=TD5C@So<}?}uA}PP7NFYY$MMN4vPb7jo
zq;(pF?LZh#qMuWciBl;e4M0-#aG^MbBGFj97qy1;W&mn}QyWs7MZy@o2kFUv1`h)@j6vBqGrudII>oYGkUZe;=Vz)ukuex>HyY30$C{$61|LmX|-dX}wK_m}PG!V_Ruk{)s{O?E>M#NEC
zPn0wza6u5q|M3YCktiG)L~)4_iSfE;NhFsDk&ww{3*o3B5+{=6@qdMeU|o?E34=U8
zrqn0DAu@#{GJkBFKqN&1k@-}T6g^xhKozNeF6FW&_cUhFPkfNOf!pG`~m)2ZZ-G
z;SL?6!rDJ_2p32vk-}|o#ZRXYjt;UQ5pkIoMG{pe>WCE1f{TPqcM^%73@w>_Bq1Cf
zL_#JXq506NsPirlYjsZuM+9MziNLj9lA*O0iNYB{92ZE;*N+MjaYDG~PO6Uz60ySB
esHc%}x%>qcRVPyI3hlc90000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_checkin_venue_visit.xml b/app/src/main/res/layout/item_checkin_venue_visit.xml
new file mode 100644
index 000000000..29585483b
--- /dev/null
+++ b/app/src/main/res/layout/item_checkin_venue_visit.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_checkin_venue_visits_day_header.xml b/app/src/main/res/layout/item_checkin_venue_visits_day_header.xml
new file mode 100644
index 000000000..aac69e378
--- /dev/null
+++ b/app/src/main/res/layout/item_checkin_venue_visits_day_header.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f4c8b4795..dc3966307 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -85,6 +85,6 @@
#f2f2f2#c0f2f2f2
-
+ #fdedf0
diff --git a/app/src/main/res/values/crowdnotifier_styles.xml b/app/src/main/res/values/crowdnotifier_styles.xml
index 6672bfd69..5ea6611bc 100644
--- a/app/src/main/res/values/crowdnotifier_styles.xml
+++ b/app/src/main/res/values/crowdnotifier_styles.xml
@@ -18,6 +18,17 @@
@color/light_grey
+
+
+
+
From ff73cbd720f0fde4f5f1b2bc4a643de60a9547d7 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 14 May 2021 17:46:38 +0200
Subject: [PATCH 038/207] Removed Tracing disabled error state from Home Screen
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 37 +-----------------
.../bag/dp3t/home/TracingBoxFragment.java | 8 ++--
.../bag/dp3t/home/model/TracingState.java | 7 ++--
.../admin/bag/dp3t/home/views/HeaderView.java | 14 ++-----
.../bag/dp3t/util/TracingStatusHelper.java | 38 ++++++++-----------
5 files changed, 27 insertions(+), 77 deletions(-)
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 4ea8c6332..3b84cab65 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
@@ -11,7 +11,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
@@ -41,7 +40,6 @@
import java.util.concurrent.TimeUnit;
import org.dpppt.android.sdk.TracingStatus;
-import org.dpppt.android.sdk.internal.logger.Logger;
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.R;
@@ -52,7 +50,6 @@
import ch.admin.bag.dp3t.contacts.ContactsFragment;
import ch.admin.bag.dp3t.home.model.NotificationState;
import ch.admin.bag.dp3t.home.model.NotificationStateError;
-import ch.admin.bag.dp3t.home.model.TracingState;
import ch.admin.bag.dp3t.home.model.TracingStatusInterface;
import ch.admin.bag.dp3t.home.views.HeaderView;
import ch.admin.bag.dp3t.inform.InformActivity;
@@ -230,8 +227,7 @@ private void setupInfobox() {
private void setupTracingView() {
TypedValue outValue = new TypedValue();
- requireContext().getTheme().resolveAttribute(
- android.R.attr.selectableItemBackground, outValue, true);
+ requireContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
tracingCard.setForeground(requireContext().getDrawable(outValue.resourceId));
tracingViewModel.getAppStatusLiveData().observe(getViewLifecycleOwner(), tracingStatusInterface -> {
@@ -291,14 +287,7 @@ public void onAnimationEnd(Animator animation) {
}
TracingStatus.ErrorState errorState = tracingStatusInterface.getReportErrorState();
- if (tracingStatusInterface.getTracingState().equals(TracingState.NOT_ACTIVE) &&
- !tracingStatusInterface.isReportedAsInfected()) {
- NotificationErrorStateHelper
- .updateNotificationErrorView(reportErrorView, NotificationStateError.TRACING_DEACTIVATED);
- reportErrorView.findViewById(R.id.error_status_button).setOnClickListener(v -> {
- enableTracing();
- });
- } else if (errorState != null) {
+ if (errorState != null) {
TracingErrorStateHelper
.updateErrorView(reportErrorView, errorState);
reportErrorView.findViewById(R.id.error_status_button).setOnClickListener(v -> {
@@ -532,26 +521,4 @@ public void onChanged(TracingStatusInterface tracingStatusInterface) {
tracingViewModel.getAppStatusLiveData().observe(getViewLifecycleOwner(), observer);
}
- private void enableTracing() {
- Activity activity = getActivity();
- if (activity == null) {
- return;
- }
-
- tracingViewModel.enableTracing(activity,
- () -> { },
- e -> {
- String message = ENExceptionHelper.getErrorMessage(e, activity);
- Logger.e(TAG, message);
- new AlertDialog.Builder(activity, R.style.NextStep_AlertDialogStyle)
- .setTitle(R.string.android_en_start_failure)
- .setMessage(message)
- .setPositiveButton(R.string.android_button_ok, (dialog, which) -> {})
- .show();
- },
- () -> {
- // cancelled
- });
- }
-
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/TracingBoxFragment.java b/app/src/main/java/ch/admin/bag/dp3t/home/TracingBoxFragment.java
index bb1ad7aa5..9a35a8c1a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/TracingBoxFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/TracingBoxFragment.java
@@ -91,19 +91,17 @@ private void showStatus() {
} else if (tracingStatusInterface.isReportedAsInfected()) {
tracingStatusView.setVisibility(View.VISIBLE);
tracingErrorView.setVisibility(View.GONE);
- TracingStatusHelper.updateStatusView(tracingStatusView, TracingState.ENDED, isHomeFragment);
+ TracingStatusHelper.updateStatusView(tracingStatusView, TracingState.ENDED);
} else if (!isTracing) {
tracingStatusView.setVisibility(View.GONE);
tracingErrorView.setVisibility(View.VISIBLE);
TracingStatusHelper.showTracingDeactivated(tracingErrorView, isHomeFragment);
TextView buttonView = tracingErrorView.findViewById(R.id.error_status_button);
- buttonView.setOnClickListener(v -> {
- enableTracing();
- });
+ buttonView.setOnClickListener(v -> enableTracing());
} else {
tracingStatusView.setVisibility(View.VISIBLE);
tracingErrorView.setVisibility(View.GONE);
- TracingStatusHelper.updateStatusView(tracingStatusView, TracingState.ACTIVE, isHomeFragment);
+ TracingStatusHelper.updateStatusView(tracingStatusView, TracingState.ACTIVE);
}
});
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/model/TracingState.java b/app/src/main/java/ch/admin/bag/dp3t/home/model/TracingState.java
index 8df30ca50..c65846a3d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/model/TracingState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/model/TracingState.java
@@ -34,13 +34,12 @@ int getTitle(TracingState tracingState) {
}
public static @StringRes
- int getText(TracingState tracingState, boolean isHomeFragment) {
+ int getText(TracingState tracingState) {
switch (tracingState) {
case ACTIVE:
return R.string.tracing_active_text;
case NOT_ACTIVE:
- if (isHomeFragment) return R.string.tracing_turned_off_text;
- else return R.string.tracing_turned_off_detailed_text;
+ return R.string.tracing_turned_off_detailed_text;
case ENDED:
return R.string.tracing_ended_text;
}
@@ -53,7 +52,7 @@ int getIcon(TracingState tracingState) {
case ACTIVE:
return R.drawable.ic_check;
case NOT_ACTIVE:
- return R.drawable.ic_warning_red;
+ return R.drawable.ic_info;
case ENDED:
return R.drawable.ic_stopp;
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/views/HeaderView.java b/app/src/main/java/ch/admin/bag/dp3t/home/views/HeaderView.java
index 346b5ff4e..ac85cec89 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/views/HeaderView.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/views/HeaderView.java
@@ -141,16 +141,10 @@ public void setState(TracingStatusInterface state) {
iconRes = R.drawable.ic_warning;
}
} else {
- if (state.getTracingState() == TracingState.ACTIVE) {
- iconRes = R.drawable.ic_begegnungen;
- iconTintColor = R.color.white;
- iconBgRes = R.drawable.bg_header_icon_on;
- backgroundColor = getResources().getColor(R.color.header_bg_on, null);
- } else {
- iconRes = R.drawable.ic_warning_red;
- iconBgRes = R.drawable.bg_header_icon_off;
- backgroundColor = getResources().getColor(R.color.header_bg_off, null);
- }
+ iconRes = R.drawable.ic_begegnungen;
+ iconTintColor = R.color.white;
+ iconBgRes = R.drawable.bg_header_icon_on;
+ backgroundColor = getResources().getColor(R.color.header_bg_on, null);
}
} else if (state.getNotificationState() == NotificationState.POSITIVE_TESTED) {
backgroundColor = getResources().getColor(R.color.header_bg_exposed, null);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/TracingStatusHelper.java b/app/src/main/java/ch/admin/bag/dp3t/util/TracingStatusHelper.java
index 930ae7d4f..3f7661048 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/TracingStatusHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/TracingStatusHelper.java
@@ -17,17 +17,18 @@
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
+import androidx.core.widget.ImageViewCompat;
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.home.model.TracingState;
public class TracingStatusHelper {
- public static void updateStatusView(View statusView, TracingState state, boolean isHomeFragment) {
- updateStatusView(statusView, state, true, isHomeFragment);
+ public static void updateStatusView(View statusView, TracingState state) {
+ updateStatusView(statusView, state, true);
}
- public static void updateStatusView(View statusView, TracingState state, boolean displayIllu, boolean isHomeFragment) {
+ public static void updateStatusView(View statusView, TracingState state, boolean displayIllu) {
Context context = statusView.getContext();
if (TracingState.getBackgroundColor(state) != -1) {
statusView.findViewById(R.id.status_background)
@@ -49,8 +50,8 @@ public static void updateStatusView(View statusView, TracingState state, boolean
} else {
titleView.setVisibility(View.GONE);
}
- if (TracingState.getText(state, isHomeFragment) != -1) {
- textView.setText(TracingState.getText(state, isHomeFragment));
+ if (TracingState.getText(state) != -1) {
+ textView.setText(TracingState.getText(state));
textView.setVisibility(View.VISIBLE);
} else {
textView.setVisibility(View.GONE);
@@ -76,28 +77,19 @@ public static void updateStatusView(View statusView, TracingState state, boolean
}
public static void showTracingDeactivated(View tracingErrorView, boolean isHomeFragment) {
+ int darkMainColor = ContextCompat.getColor(tracingErrorView.getContext(), R.color.dark_main);
+
ImageView iconView = tracingErrorView.findViewById(R.id.error_status_image);
- if (TracingState.getIcon(TracingState.NOT_ACTIVE) != -1) {
- iconView.setImageResource(TracingState.getIcon(TracingState.NOT_ACTIVE));
- iconView.setVisibility(View.VISIBLE);
- } else {
- iconView.setVisibility(View.GONE);
- }
+ iconView.setImageResource(R.drawable.ic_info);
+ ImageViewCompat.setImageTintList(iconView, ColorStateList.valueOf(darkMainColor));
+
TextView titleView = tracingErrorView.findViewById(R.id.error_status_title);
- if (TracingState.getTitle(TracingState.NOT_ACTIVE) != -1) {
- titleView.setText(TracingState.getTitle(TracingState.NOT_ACTIVE));
- titleView.setVisibility(View.VISIBLE);
- } else {
- titleView.setVisibility(View.GONE);
- }
+ titleView.setText(TracingState.getTitle(TracingState.NOT_ACTIVE));
+ titleView.setTextColor(darkMainColor);
TextView textView = tracingErrorView.findViewById(R.id.error_status_text);
- if (TracingState.getText(TracingState.NOT_ACTIVE, isHomeFragment) != -1) {
- textView.setText(TracingState.getText(TracingState.NOT_ACTIVE, isHomeFragment));
- textView.setVisibility(View.VISIBLE);
- } else {
- textView.setVisibility(View.GONE);
- }
+ textView.setText(TracingState.getText(TracingState.NOT_ACTIVE));
+
tracingErrorView.findViewById(R.id.error_status_code).setVisibility(View.GONE);
TextView buttonView = tracingErrorView.findViewById(R.id.error_status_button);
if (isHomeFragment) {
From f5ac74011974b9f72f1aa822fde10c375c3718f3 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 13 May 2021 15:48:03 +0200
Subject: [PATCH 039/207] Share PDF with link
---
.../checkin/generateqrcode/QrCodeFragment.kt | 21 ++++++++-----------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index 734ecaab9..d35edf8ee 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -14,14 +14,17 @@ import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import ch.admin.bag.dp3t.BuildConfig
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment
import ch.admin.bag.dp3t.checkin.models.CheckInState
import ch.admin.bag.dp3t.checkin.models.QRCodePayload
+import ch.admin.bag.dp3t.checkin.utils.getSubtitle
import ch.admin.bag.dp3t.checkin.utils.toQrCodePayload
import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
+import ch.admin.bag.dp3t.util.showFragment
import com.google.protobuf.ByteString
import org.crowdnotifier.android.sdk.model.VenueInfo
import java.io.File
@@ -45,7 +48,7 @@ class QrCodeFragment : Fragment() {
cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
val venueInfo = QRCodePayload.parseFrom(arguments?.get(KEY_VENUE_INFO) as ByteString).toVenueInfo()
titleTextview.text = venueInfo.title
- subtitleTextview.text = "TODO"
+ subtitleTextview.text = venueInfo.getSubtitle()
qrCodeImageview.visibility = View.INVISIBLE
qrCodeLoadingProgressbar.isVisible = true
shareButton.isEnabled = false
@@ -59,7 +62,7 @@ class QrCodeFragment : Fragment() {
qrCodeViewModel.selectedQrCodePdf.observe(viewLifecycleOwner) { pdfFile ->
shareButton.isEnabled = true
printPdfButton.isEnabled = true
- shareButton.setOnClickListener { sharePdf(pdfFile) }
+ shareButton.setOnClickListener { sharePdf(pdfFile, venueInfo) }
printPdfButton.setOnClickListener { printPdf(pdfFile) }
}
qrCodeViewModel.generateQrCodeBitmap(venueInfo)
@@ -73,7 +76,7 @@ class QrCodeFragment : Fragment() {
checkinButton.setOnClickListener {
crowdNotifierViewModel.checkInState =
CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), 0)
- showCheckInFragment()
+ showFragment(CheckInFragment.newInstance())
}
}.root
}
@@ -87,24 +90,18 @@ class QrCodeFragment : Fragment() {
}
}
- private fun sharePdf(file: File) {
+ private fun sharePdf(file: File, venueInfo: VenueInfo) {
context?.let {
val pdfUri: Uri = FileProvider.getUriForFile(it, it.applicationContext.packageName.toString() + ".provider", file)
Intent().apply {
action = Intent.ACTION_SEND
type = "application/pdf"
+ putExtra(Intent.EXTRA_TEXT, venueInfo.toQrCodeString(BuildConfig.ENTRY_QR_CODE_PREFIX))
putExtra(Intent.EXTRA_STREAM, pdfUri)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(this)
}
}
}
-
- private fun showCheckInFragment() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, CheckInFragment.newInstance())
- .addToBackStack(CheckInFragment::class.java.canonicalName)
- .commitAllowingStateLoss()
- }
}
\ No newline at end of file
From 14e0c7a0af21af2b353e466857133279e7e61fff Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 13 May 2021 16:30:23 +0200
Subject: [PATCH 040/207] Implemented confirmation dialog for deleting QR Codes
---
.../dp3t/checkin/CheckinOverviewFragment.kt | 34 ++++---------------
.../generateqrcode/EventsOverviewFragment.kt | 28 ++++++---------
.../checkin/generateqrcode/QrCodeAdapter.kt | 3 +-
.../checkin/generateqrcode/QrCodeFragment.kt | 15 ++++++--
.../res/layout/fragment_events_overview.xml | 2 +-
app/src/main/res/layout/fragment_qr_code.xml | 2 +-
app/src/main/res/layout/item_qr_code.xml | 23 ++++++++-----
app/src/main/res/values-bs/strings.xml | 3 ++
app/src/main/res/values-de/strings.xml | 3 ++
app/src/main/res/values-es/strings.xml | 3 ++
app/src/main/res/values-fr/strings.xml | 3 ++
app/src/main/res/values-hr/strings.xml | 3 ++
app/src/main/res/values-it/strings.xml | 3 ++
app/src/main/res/values-pt/strings.xml | 3 ++
app/src/main/res/values-rm/strings.xml | 3 ++
app/src/main/res/values-sq/strings.xml | 3 ++
app/src/main/res/values-sr/strings.xml | 3 ++
app/src/main/res/values-ti/strings.xml | 3 ++
app/src/main/res/values-tr/strings.xml | 3 ++
app/src/main/res/values/strings.xml | 3 ++
app/src/main/res/values/styles.xml | 1 +
21 files changed, 88 insertions(+), 59 deletions(-)
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 a59a4a587..b44e5bdc2 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
@@ -16,6 +16,7 @@ import ch.admin.bag.dp3t.checkin.checkinflow.QrCodeScannerFragment
import ch.admin.bag.dp3t.checkin.diary.DiaryFragment
import ch.admin.bag.dp3t.databinding.FragmentCheckinOverviewBinding
import ch.admin.bag.dp3t.util.StringUtil
+import ch.admin.bag.dp3t.util.showFragment
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
class CheckinOverviewFragment : Fragment() {
@@ -31,7 +32,7 @@ class CheckinOverviewFragment : Fragment() {
private val tracingViewModel: TracingViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return FragmentCheckinOverviewBinding.inflate(layoutInflater).apply {
+ return FragmentCheckinOverviewBinding.inflate(inflater).apply {
checkinOverviewToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
checkinOverviewDiary.setOnClickListener { authenticateAndShowDiary() }
@@ -49,8 +50,8 @@ class CheckinOverviewFragment : Fragment() {
}
})
- checkinButton.setOnClickListener { showQrCodeScannerFragment() }
- checkoutButton.setOnClickListener { showCheckOutFragment() }
+ checkinButton.setOnClickListener { showFragment(QrCodeScannerFragment.newInstance()) }
+ checkoutButton.setOnClickListener { showFragment(CheckOutFragment.newInstance()) }
crowdNotifierViewModel.timeSinceCheckIn.observe(viewLifecycleOwner) { duration ->
checkinTime.text = StringUtil.getShortDurationString(duration)
@@ -58,22 +59,6 @@ class CheckinOverviewFragment : Fragment() {
}.root
}
- private fun showCheckOutFragment() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, CheckOutFragment.newInstance())
- .addToBackStack(CheckOutFragment::class.java.canonicalName)
- .commit()
- }
-
- private fun showQrCodeScannerFragment() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, QrCodeScannerFragment.newInstance())
- .addToBackStack(QrCodeScannerFragment::class.java.canonicalName)
- .commit()
- }
-
private fun authenticateAndShowDiary() {
val biometricState = BiometricManager.from(requireContext())
.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK)
@@ -81,7 +66,7 @@ class CheckinOverviewFragment : Fragment() {
val executor = ContextCompat.getMainExecutor(requireContext())
val biometricPrompt = BiometricPrompt(requireActivity(), executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
- showDiaryFragment()
+ showFragment(DiaryFragment.newInstance())
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
@@ -92,15 +77,8 @@ class CheckinOverviewFragment : Fragment() {
.build()
biometricPrompt.authenticate(promptInfo)
} else {
- showDiaryFragment()
+ showFragment(DiaryFragment.newInstance())
}
}
- private fun showDiaryFragment() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, DiaryFragment.newInstance())
- .addToBackStack(DiaryFragment::class.java.canonicalName)
- .commit()
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
index 080c10ab7..e5cd37956 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
@@ -4,10 +4,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentEventsOverviewBinding
+import ch.admin.bag.dp3t.util.showFragment
import org.crowdnotifier.android.sdk.model.VenueInfo
class EventsOverviewFragment : Fragment() {
@@ -24,15 +26,15 @@ class EventsOverviewFragment : Fragment() {
val adapter = QrCodeAdapter(object : OnClickListener {
override fun generateQrCode() {
- showGenerateQrCodeScreen()
+ showFragment(GenerateQrCodeFragment.newInstance())
}
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
- showQrCodeFragment(qrCodeItem)
+ showFragment(QrCodeFragment.newInstance(qrCodeItem))
}
override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
- qrCodeViewModel.deleteQrCode(qrCodeItem)
+ showDeleteConfirmationDialog(qrCodeItem)
}
})
@@ -44,20 +46,12 @@ class EventsOverviewFragment : Fragment() {
}.root
}
- private fun showQrCodeFragment(venueInfo: VenueInfo) {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, QrCodeFragment.newInstance(venueInfo))
- .addToBackStack(QrCodeFragment::class.java.canonicalName)
- .commit()
- }
-
- private fun showGenerateQrCodeScreen() {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, GenerateQrCodeFragment.newInstance())
- .addToBackStack(GenerateQrCodeFragment::class.java.canonicalName)
- .commit()
+ private fun showDeleteConfirmationDialog(venueInfo: VenueInfo) {
+ AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.delete_qr_code_dialog)
+ .setPositiveButton(R.string.delete_button_title) { _, _ -> qrCodeViewModel.deleteQrCode(venueInfo) }
+ .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
+ .show()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
index f91632cab..062030c6f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
@@ -3,6 +3,7 @@ package ch.admin.bag.dp3t.checkin.generateqrcode
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
+import ch.admin.bag.dp3t.checkin.utils.getSubtitle
import ch.admin.bag.dp3t.databinding.ItemGenerateQrCodeBinding
import ch.admin.bag.dp3t.databinding.ItemQrCodeBinding
import ch.admin.bag.dp3t.databinding.ItemQrCodeEmptyListBinding
@@ -88,7 +89,7 @@ class QrCodeAdapter(val onClickListener: OnClickListener) : RecyclerView.Adapter
override fun bind(item: VenueInfo) {
binding.apply {
qrCodeName.text = item.title
- qrCodeLocation.text = "TODO"
+ qrCodeLocation.text = item.getSubtitle()
root.setOnClickListener { onClickListener.onQrCodeClicked(item) }
qrCodeDelete.setOnClickListener { onClickListener.onDeleteQrCodeClicked(item) }
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index d35edf8ee..ef67a5dd1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -9,6 +9,7 @@ import android.print.PrintManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
@@ -67,8 +68,7 @@ class QrCodeFragment : Fragment() {
}
qrCodeViewModel.generateQrCodeBitmap(venueInfo)
deleteButton.setOnClickListener {
- qrCodeViewModel.deleteQrCode(venueInfo)
- requireActivity().supportFragmentManager.popBackStack()
+ showDeleteConfirmationDialog(venueInfo)
}
crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner) { isCheckedIn ->
checkinButton.isEnabled = !isCheckedIn
@@ -104,4 +104,15 @@ class QrCodeFragment : Fragment() {
}
}
}
+
+ private fun showDeleteConfirmationDialog(venueInfo: VenueInfo) {
+ AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.delete_qr_code_dialog)
+ .setPositiveButton(R.string.delete_button_title) { _, _ ->
+ qrCodeViewModel.deleteQrCode(venueInfo)
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
+ .show()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_events_overview.xml b/app/src/main/res/layout/fragment_events_overview.xml
index 875c50fd3..3f6b08a9c 100644
--- a/app/src/main/res/layout/fragment_events_overview.xml
+++ b/app/src/main/res/layout/fragment_events_overview.xml
@@ -26,7 +26,7 @@
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
index 3ebef1eae..6f9c4403c 100644
--- a/app/src/main/res/layout/fragment_qr_code.xml
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -100,7 +100,7 @@
+ android:foreground="?selectableItemBackground">
+ tools:text="UB E21.7" />
+ tools:text="Sitzungsraum" />
+
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index a35cedd8c..a9a0678b4 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -1666,4 +1666,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:""Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 0cbea7657..779eafc72 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1731,4 +1731,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:""Schützen Sie sich selber und andere indem Sie QR Codes für Arbeitsplätze, Konferenzräume oder andere private Events erstellen."
+
+
+ "Möchten Sie den QR Code wirklich löschen?"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 616f2feb9..6d14702fe 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1657,4 +1657,7 @@ Contacte con la Infoline del coronavirus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 79cfc9508..a595af16b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1692,4 +1692,7 @@ Contactez l'infoline coronavirus :"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 619b7f5c9..bb9daf4fe 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1662,4 +1662,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 1925da572..8f0ae136d 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1700,4 +1700,7 @@ Contatta la Infoline coronavirus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 496afa74c..7842fd1ac 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1667,4 +1667,7 @@ Contacte a linha de informação coronavírus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index ce603cd7a..44dc2b0b6 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1662,4 +1662,7 @@ Alura contactai la Infoline coronavirus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 40c0f0cfb..704916862 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1667,4 +1667,7 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index a94813b0b..532765cae 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1662,4 +1662,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index 13a3ad5fd..ffded7f03 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1642,4 +1642,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index b7fadd7c5..d50f3a872 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1644,4 +1644,7 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b7a4dd850..59971f09c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1689,4 +1689,7 @@ If so, contact the Coronavirus Infoline:"
"Protect yourself and others by generating QR codes for working spaces, meeting rooms or other private events."
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index f99a5ad1e..f7479d3a5 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -254,6 +254,7 @@
From b179a0314fc723a79acd179f318d856b3f3401f5 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 13 May 2021 17:17:30 +0200
Subject: [PATCH 041/207] QR-Code Screen Clean Ups
---
.../checkin/generateqrcode/QrCodeFragment.kt | 12 +++++++---
.../admin/bag/dp3t/util/LiveDataExtensions.kt | 11 ++++++++++
.../{ic_print_pdf.xml => ic_print.xml} | 2 +-
app/src/main/res/layout/fragment_qr_code.xml | 22 +++++++++----------
app/src/main/res/values-bs/strings.xml | 3 +++
app/src/main/res/values-de/strings.xml | 3 +++
app/src/main/res/values-es/strings.xml | 3 +++
app/src/main/res/values-fr/strings.xml | 3 +++
app/src/main/res/values-hr/strings.xml | 3 +++
app/src/main/res/values-it/strings.xml | 3 +++
app/src/main/res/values-pt/strings.xml | 3 +++
app/src/main/res/values-rm/strings.xml | 3 +++
app/src/main/res/values-sq/strings.xml | 3 +++
app/src/main/res/values-sr/strings.xml | 3 +++
app/src/main/res/values-ti/strings.xml | 3 +++
app/src/main/res/values-tr/strings.xml | 3 +++
app/src/main/res/values/strings.xml | 3 +++
17 files changed, 71 insertions(+), 15 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/LiveDataExtensions.kt
rename app/src/main/res/drawable/{ic_print_pdf.xml => ic_print.xml} (56%)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index ef67a5dd1..c65b5ef81 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -25,7 +25,9 @@ import ch.admin.bag.dp3t.checkin.utils.getSubtitle
import ch.admin.bag.dp3t.checkin.utils.toQrCodePayload
import ch.admin.bag.dp3t.checkin.utils.toVenueInfo
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
+import ch.admin.bag.dp3t.util.combineWith
import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import com.google.protobuf.ByteString
import org.crowdnotifier.android.sdk.model.VenueInfo
import java.io.File
@@ -42,11 +44,12 @@ class QrCodeFragment : Fragment() {
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
private val crowdNotifierViewModel: CrowdNotifierViewModel by activityViewModels()
+ private val tracingViewModel: TracingViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
- cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
val venueInfo = QRCodePayload.parseFrom(arguments?.get(KEY_VENUE_INFO) as ByteString).toVenueInfo()
titleTextview.text = venueInfo.title
subtitleTextview.text = venueInfo.getSubtitle()
@@ -70,8 +73,11 @@ class QrCodeFragment : Fragment() {
deleteButton.setOnClickListener {
showDeleteConfirmationDialog(venueInfo)
}
- crowdNotifierViewModel.isCheckedIn.observe(viewLifecycleOwner) { isCheckedIn ->
- checkinButton.isEnabled = !isCheckedIn
+ crowdNotifierViewModel.isCheckedIn.combineWith(tracingViewModel.appStatusLiveData) { isCheckedIn, appStatusInterface ->
+ // show self-checkin button only if user is not in isolation and is not checked in already
+ appStatusInterface?.isReportedAsInfected == false && isCheckedIn == false
+ }.observe(viewLifecycleOwner) { showCheckinButton ->
+ checkinButton.isVisible = showCheckinButton
}
checkinButton.setOnClickListener {
crowdNotifierViewModel.checkInState =
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/LiveDataExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/util/LiveDataExtensions.kt
new file mode 100644
index 000000000..77a05a502
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/LiveDataExtensions.kt
@@ -0,0 +1,11 @@
+package ch.admin.bag.dp3t.util
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+
+fun LiveData.combineWith(liveData: LiveData, block: (T?, K?) -> R): LiveData {
+ val result = MediatorLiveData()
+ result.addSource(this) { result.value = block(this.value, liveData.value) }
+ result.addSource(liveData) { result.value = block(this.value, liveData.value) }
+ return result
+}
diff --git a/app/src/main/res/drawable/ic_print_pdf.xml b/app/src/main/res/drawable/ic_print.xml
similarity index 56%
rename from app/src/main/res/drawable/ic_print_pdf.xml
rename to app/src/main/res/drawable/ic_print.xml
index 4175e1d9d..79355cf2f 100644
--- a/app/src/main/res/drawable/ic_print_pdf.xml
+++ b/app/src/main/res/drawable/ic_print.xml
@@ -4,7 +4,7 @@
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/app/src/main/res/layout/fragment_qr_code.xml b/app/src/main/res/layout/fragment_qr_code.xml
index 6f9c4403c..f73f4193d 100644
--- a/app/src/main/res/layout/fragment_qr_code.xml
+++ b/app/src/main/res/layout/fragment_qr_code.xml
@@ -5,16 +5,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
- android:orientation="vertical"
- android:padding="@dimen/spacing_large">
+ android:orientation="vertical">
-
+ app:layout_constraintTop_toTopOf="parent"
+ app:navigationContentDescription="@string/android_accessibility_back_button"
+ app:navigationIcon="@drawable/ic_arrow_back" />
+ android:orientation="vertical"
+ android:padding="@dimen/spacing_large">
+ android:text="@string/self_checkin_button_title" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 779eafc72..06e49f3d4 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1734,4 +1734,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Möchten Sie den QR Code wirklich löschen?"
+
+
+ "Selbst einchecken"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 6d14702fe..8e6871156 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1660,4 +1660,7 @@ Contacte con la Infoline del coronavirus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index a595af16b..63187c9d0 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1695,4 +1695,7 @@ Contactez l'infoline coronavirus :"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index bb9daf4fe..7209e4428 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1665,4 +1665,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 8f0ae136d..cf6c1f31a 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1703,4 +1703,7 @@ Contatta la Infoline coronavirus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 7842fd1ac..85688d622 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1670,4 +1670,7 @@ Contacte a linha de informação coronavírus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 44dc2b0b6..e335a6c95 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1665,4 +1665,7 @@ Alura contactai la Infoline coronavirus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 704916862..5f3ba732c 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1670,4 +1670,7 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 532765cae..30c259615 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1665,4 +1665,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index ffded7f03..920c2ded7 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1645,4 +1645,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index d50f3a872..71d409b83 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1647,4 +1647,7 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 59971f09c..ed867215d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1692,4 +1692,7 @@ If so, contact the Coronavirus Infoline:"
+
+
+
\ No newline at end of file
From 0ad9d1ba957c55d55f90d55d1064e3fb0785569d Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 13 May 2021 17:25:03 +0200
Subject: [PATCH 042/207] Update Entry QR-Code URLs
---
app/build.gradle | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3d9d1e094..83ea09c2d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -71,7 +71,7 @@ android {
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-d.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
- buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-d.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.dev.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.dev.BUCKET_PUBLIC_KEY}\""
@@ -85,7 +85,7 @@ android {
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-t.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-t.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
- buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-t.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.test.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.test.BUCKET_PUBLIC_KEY}\""
@@ -99,7 +99,7 @@ android {
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-a.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
- buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-a.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.abnahme.BUCKET_PUBLIC_KEY}\""
@@ -113,7 +113,7 @@ android {
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
- buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
@@ -126,7 +126,7 @@ android {
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
- buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-dev.notify-me.ch"'
+ buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
From 23b2fa22dca25ebc6e938b62c4232ec9afa18397 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 13 May 2021 17:32:26 +0200
Subject: [PATCH 043/207] Add icon to check-in button
---
app/src/main/res/layout/fragment_check_in.xml | 1 +
app/src/main/res/values/styles.xml | 1 +
2 files changed, 2 insertions(+)
diff --git a/app/src/main/res/layout/fragment_check_in.xml b/app/src/main/res/layout/fragment_check_in.xml
index 9ce96bfe8..a4bc061ca 100644
--- a/app/src/main/res/layout/fragment_check_in.xml
+++ b/app/src/main/res/layout/fragment_check_in.xml
@@ -110,6 +110,7 @@
android:layout_gravity="center"
android:layout_marginHorizontal="@dimen/spacing_larger"
android:layout_marginVertical="@dimen/spacing_larger"
+ android:drawableEnd="@drawable/ic_check_in"
android:text="@string/check_in_now_button_title" />
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index f7479d3a5..40e7fa04b 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -190,6 +190,7 @@
+
+
From 6869454e226e7e92c5128f68d393d1c0e6caf4da Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 20 May 2021 17:01:24 +0200
Subject: [PATCH 059/207] Fix timezone issues
---
.../ch/admin/bag/dp3t/debug/DebugFragment.java | 9 +++------
.../dp3t/checkin/CrowdNotifierViewModel.java | 2 +-
.../dp3t/extensions/ExposureEventExtensions.kt | 3 +--
.../admin/bag/dp3t/reports/ReportsFragment.kt | 14 ++++++++++++--
.../bag/dp3t/reports/ReportsRecyclerAdapter.kt | 9 ++++++++-
.../java/ch/admin/bag/dp3t/util/DateUtils.java | 17 +++++++++++++++--
.../java/ch/admin/bag/dp3t/util/StringUtil.java | 17 ++---------------
7 files changed, 42 insertions(+), 29 deletions(-)
diff --git a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
index 511709766..e99157a80 100644
--- a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
+++ b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
@@ -37,7 +37,6 @@
import org.dpppt.android.sdk.models.ExposureDay;
import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
import ch.admin.bag.dp3t.checkin.models.SwissCovidAssociatedData;
import ch.admin.bag.dp3t.debug.model.DebugAppState;
import ch.admin.bag.dp3t.networking.CertificatePinning;
@@ -50,7 +49,6 @@ public class DebugFragment extends Fragment {
public static final boolean EXISTS = true;
private TracingViewModel tracingViewModel;
- private CrowdNotifierViewModel crowdNotifierViewModel;
public static void startDebugFragment(FragmentManager parentFragmentManager) {
parentFragmentManager.beginTransaction()
@@ -72,7 +70,6 @@ public DebugFragment() {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tracingViewModel = new ViewModelProvider(requireActivity()).get(TracingViewModel.class);
- crowdNotifierViewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
}
@Override
@@ -163,8 +160,8 @@ private void showInputDialog(String title, String defaultInput, InputDialogCallb
}
private void showExposureDaysInputDialogs() {
- showInputDialog(getString(R.string.number_of_exposure_days), "0", (tracingExposures) ->
- showInputDialog("How many Checkin Exposures should be simulated?", "1", (checkinExposures) ->
+ showInputDialog(getString(R.string.number_of_exposure_days), "2", (tracingExposures) ->
+ showInputDialog("How many Checkin Exposures should be simulated?", "2", (checkinExposures) ->
exposeMyself(tracingExposures, checkinExposures)));
}
@@ -182,7 +179,7 @@ private void exposeMyself(int numberOfDays, int numberOfCheckins) {
ExposureStorage exposureStorage = ExposureStorage.getInstance(requireContext());
exposureStorage.clear();
for (int i = 0; i < numberOfCheckins; i++) {
- long exposureStart = new DayDate().subtractDays(i).getStartOfDay(TimeZone.getDefault()) + 1000L * 60 * 60 * 12;
+ long exposureStart = new DayDate().subtractDays(i).getStartOfDay(TimeZone.getDefault());
long exposureEnd = exposureStart + 1000L * 60 * 60;
exposureStorage.addEntry(new ExposureEvent(-i, exposureStart, exposureEnd, "debug message",
SwissCovidAssociatedData.getDefaultInstance().toByteArray()));
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 9728b55e1..feca7f8db 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
@@ -187,7 +187,7 @@ public ExposureEvent getLatestExposure() {
public long getDaysSinceExposure() {
ExposureEvent latestExposure = getLatestExposure();
if (latestExposure == null) return -1;
- return DateUtils.getDaysDiff(getLatestExposure().getEndTime());
+ return Math.max(0, DateUtils.getDaysDiff(getLatestExposure().getEndTime()));
}
public long getSelectedReminderDelay() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
index d8030afd6..8f0dd2706 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
@@ -3,10 +3,9 @@ package ch.admin.bag.dp3t.extensions
import android.content.Context
import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.model.ExposureEvent
-import org.dpppt.android.sdk.models.DayDate
fun ExposureEvent.getDetailsString(context: Context): String {
- return "${StringUtil.getReportDateString(DayDate(endTime), true, false, context)}\n${
+ return "${StringUtil.getReportDateString(endTime, true, false, context)}\n${
StringUtil.getHourMinuteTimeString(startTime, ":")
} - ${StringUtil.getHourMinuteTimeString(endTime, ":")}"
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
index 05e403a3a..0e22730d5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
@@ -133,14 +133,24 @@ class ReportsFragment : Fragment() {
} else {
if (exposureDays.isNotEmpty()) {
headerDate.text =
- StringUtil.getReportDateString(exposureDays[exposureDays.size - 1].exposedDate, true, true, context)
+ StringUtil.getReportDateString(
+ exposureDays[exposureDays.size - 1].exposedDate.getStartOfDay(TimeZone.getDefault()),
+ true,
+ true,
+ context
+ )
}
datesContainer.removeAllViews()
for (exposureDay in exposureDays) {
val itemView = LayoutInflater.from(context)
.inflate(R.layout.item_reports_exposure_day, datesContainer, false)
val itemDate = itemView.findViewById(R.id.exposure_day_textview)
- itemDate.text = StringUtil.getReportDateString(exposureDay.exposedDate, false, true, context)
+ itemDate.text = StringUtil.getReportDateString(
+ exposureDay.exposedDate.getStartOfDay(TimeZone.getDefault()),
+ false,
+ true,
+ context
+ )
datesContainer.addView(itemView, 0)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsRecyclerAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsRecyclerAdapter.kt
index af4aa584b..c9fcd7c98 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsRecyclerAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsRecyclerAdapter.kt
@@ -11,6 +11,8 @@ import ch.admin.bag.dp3t.extensions.getDetailsString
import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.model.ExposureEvent
import org.dpppt.android.sdk.models.ExposureDay
+import java.util.*
+import kotlin.collections.ArrayList
class ReportsRecyclerAdapter : RecyclerView.Adapter() {
@@ -47,7 +49,12 @@ class ReportsRecyclerAdapter : RecyclerView.Adapter {
reportType.setText(R.string.meldung_detail_exposed_list_card_title_encounters)
reportDetails.text = item.exposures.joinToString("\n") {
- StringUtil.getReportDateString(it.exposedDate, true, false, root.context)
+ StringUtil.getReportDateString(
+ it.exposedDate.getStartOfDay(TimeZone.getDefault()),
+ true,
+ false,
+ root.context
+ )
}
}
is CheckinReportItem -> {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/DateUtils.java b/app/src/main/java/ch/admin/bag/dp3t/util/DateUtils.java
index 0ad6c8dc0..afd7ab5b6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/DateUtils.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/DateUtils.java
@@ -12,6 +12,7 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -24,15 +25,27 @@ public class DateUtils {
private static final DateFormat DATE_TIME_FORMAT = SimpleDateFormat.getDateTimeInstance();
private static final DateFormat DATE_FORMAT = SimpleDateFormat.getDateInstance();
- public static int getDaysDiff(long date) {
+ public static int getDaysDiff(long timestamp) {
try {
- return (int) TimeUnit.DAYS.convert(System.currentTimeMillis() - date, TimeUnit.MILLISECONDS);
+ return (int) TimeUnit.DAYS.convert(
+ getLocalStartOfDayTimestamp(System.currentTimeMillis()) - getLocalStartOfDayTimestamp(timestamp),
+ TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
+ private static long getLocalStartOfDayTimestamp(long timestamp) {
+ Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
+ calendar.setTime(new Date(timestamp));
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ return calendar.getTime().getTime();
+ }
+
public static int getDaysDiffUntil(DayDate from, DayDate to) {
try {
return (int) TimeUnit.DAYS.convert(to.getStartOfDayTimestamp() - from.getStartOfDayTimestamp(), TimeUnit.MILLISECONDS);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
index fa25adf74..89d2946c5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
@@ -25,8 +25,6 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import org.dpppt.android.sdk.models.DayDate;
-
import ch.admin.bag.dp3t.R;
public class StringUtil {
@@ -123,7 +121,7 @@ public static String getCheckOutDateString(Context context, long checkInTime, lo
}
public static String getDaysAgoString(long timeStamp, Context context) {
- final long diff = getStartOfDay().getTime() - timeStamp;
+ final long diff = DateUtils.getDaysDiff(timeStamp);
if (diff < 0) {
return context.getResources().getString(R.string.date_today);
} else {
@@ -137,8 +135,7 @@ public static String getDaysAgoString(long timeStamp, Context context) {
}
}
- public static String getReportDateString(DayDate exposedDate, boolean withDiff, boolean withPrefix, Context context) {
- long timestamp = exposedDate.getStartOfDayTimestamp();
+ public static String getReportDateString(long timestamp, boolean withDiff, boolean withPrefix, Context context) {
if (!withDiff) {
return DateUtils.getFormattedDateWrittenMonth(timestamp);
}
@@ -161,14 +158,4 @@ public static String getReportDateString(DayDate exposedDate, boolean withDiff,
return dateStr;
}
- private static Date getStartOfDay() {
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(new Date());
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- return calendar.getTime();
- }
-
}
From 14283b3ad972b1811053e8cc65c8bdf46eed8ac5 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 20 May 2021 17:10:19 +0200
Subject: [PATCH 060/207] Code cleanups
---
app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java | 4 ++--
app/src/main/java/ch/admin/bag/dp3t/MainActivity.java | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
index e99157a80..e577e3ec3 100644
--- a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
+++ b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
@@ -160,8 +160,8 @@ private void showInputDialog(String title, String defaultInput, InputDialogCallb
}
private void showExposureDaysInputDialogs() {
- showInputDialog(getString(R.string.number_of_exposure_days), "2", (tracingExposures) ->
- showInputDialog("How many Checkin Exposures should be simulated?", "2", (checkinExposures) ->
+ showInputDialog(getString(R.string.number_of_exposure_days), "2", tracingExposures ->
+ showInputDialog("How many Checkin Exposures should be simulated?", "2", checkinExposures ->
exposeMyself(tracingExposures, checkinExposures)));
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
index 6d2c7ab38..3f88dafea 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
@@ -191,7 +191,7 @@ private void handleCustomIntents() {
showReportsFragment();
} else if (ACTION_EXPOSED_GOTO_REPORTS.equals(intentAction)) {
if (tracingViewModel.getTracingStatusInterface().wasContactReportedAsExposed() ||
- crowdNotifierViewModel.getExposures().getValue().size() > 0) {
+ !crowdNotifierViewModel.getExposures().getValue().isEmpty()) {
showReportsFragment();
}
} else if (ACTION_ACTIVATE_TRACING.equals(intentAction)) {
From 7378841d6752bc28ca97cb5656b73f36eb74cea2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Mon, 24 May 2021 09:35:31 +0200
Subject: [PATCH 061/207] log all notifications if devHistory is enabled
---
app/build.gradle | 2 +-
.../admin/bag/dp3t/networking/ConfigWorker.kt | 12 ++++++++++
.../admin/bag/dp3t/util/NotificationUtil.java | 22 ++++++++++++++++++-
3 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index dbc046255..719adb4d4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -157,7 +157,7 @@ sonarqube {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
- def dp3t_sdk_version = '2.2.0'
+ def dp3t_sdk_version = '2.2.1'
devImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version-calibration"
teschtImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
abnahmeImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
index 8ffc7bd2f..1498da606 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
@@ -19,11 +19,15 @@ import androidx.core.app.NotificationCompat
import androidx.work.*
import ch.admin.bag.dp3t.BuildConfig
import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.debug.DebugFragment
import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.NotificationUtil
import org.dpppt.android.sdk.DP3T
import org.dpppt.android.sdk.backend.SignatureException
+import org.dpppt.android.sdk.internal.history.HistoryDatabase
+import org.dpppt.android.sdk.internal.history.HistoryEntry
+import org.dpppt.android.sdk.internal.history.HistoryEntryType
import org.dpppt.android.sdk.internal.logger.Logger
import java.io.IOException
import java.util.concurrent.TimeUnit
@@ -109,6 +113,14 @@ class ConfigWorker(context: Context, workerParams: WorkerParameters) : Coroutine
.setAutoCancel(true)
.build()
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ if(DebugFragment.EXISTS){
+ HistoryDatabase.getInstance(context).addEntry(
+ HistoryEntry(
+ HistoryEntryType.NOTIFICATION, "Showing update required notification", false,
+ System.currentTimeMillis()
+ )
+ )
+ }
notificationManager.notify(NotificationUtil.NOTIFICATION_ID_UPDATE, notification)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
index 016918e7f..571970fbc 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
@@ -20,8 +20,13 @@
import androidx.core.app.NotificationCompat;
import androidx.core.app.TaskStackBuilder;
+import org.dpppt.android.sdk.internal.history.HistoryDatabase;
+import org.dpppt.android.sdk.internal.history.HistoryEntry;
+import org.dpppt.android.sdk.internal.history.HistoryEntryType;
+
import ch.admin.bag.dp3t.MainActivity;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.debug.DebugFragment;
public class NotificationUtil {
@@ -59,6 +64,14 @@ public static void generateContactNotification(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ if(DebugFragment.EXISTS){
+ HistoryDatabase.getInstance(context).addEntry(
+ new HistoryEntry(
+ HistoryEntryType.NOTIFICATION, "Showing new message notification", false,
+ System.currentTimeMillis()
+ )
+ );
+ }
notificationManager.notify(NOTIFICATION_ID_CONTACT, notification);
NotificationRepeatWorker.startWorker(context);
@@ -73,7 +86,14 @@ public static void showReminderNotification(Context context) {
String message = context.getString(R.string.tracing_reminder_notification_subtitle);
Notification notification = createNotification(title, message, pendingIntent, CHANNEL_ID_REMINDER, context);
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);if(DebugFragment.EXISTS){
+ HistoryDatabase.getInstance(context).addEntry(
+ new HistoryEntry(
+ HistoryEntryType.SYNC, "Showing activate tracing notification", false,
+ System.currentTimeMillis()
+ )
+ );
+ }
notificationManager.notify(message.hashCode(), notification);
}
From 8761aee9223f9be6be24fd897a9fe741bf0741fb Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 10:31:06 +0200
Subject: [PATCH 062/207] Code cleanups
---
.../admin/bag/dp3t/extensions/ExposureEventExtensions.kt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
index 8f0dd2706..f551464f0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/ExposureEventExtensions.kt
@@ -5,8 +5,8 @@ import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.model.ExposureEvent
fun ExposureEvent.getDetailsString(context: Context): String {
- return "${StringUtil.getReportDateString(endTime, true, false, context)}\n${
- StringUtil.getHourMinuteTimeString(startTime, ":")
- } - ${StringUtil.getHourMinuteTimeString(endTime, ":")}"
-
+ val dateString = StringUtil.getReportDateString(endTime, true, false, context)
+ val startTimeString = StringUtil.getHourMinuteTimeString(startTime, ":")
+ val endTimeString = StringUtil.getHourMinuteTimeString(endTime, ":")
+ return "$dateString\n$startTimeString - $endTimeString"
}
\ No newline at end of file
From 91bb7b2c60a777b229fc9fb58a69a127c393d995 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 17:05:39 +0200
Subject: [PATCH 063/207] Fix some Report Screen issues
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 3 +-
.../dp3t/reports/ReportsOverviewFragment.kt | 36 ++++++++++++-------
2 files changed, 26 insertions(+), 13 deletions(-)
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 c69feb93b..dafc4dc8e 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
@@ -256,7 +256,8 @@ private void showContactsFragment() {
private void showReportsFragment() {
int checkinReports = crowdNotifierViewModel.getExposures().getValue().size();
int tracingReports = tracingViewModel.getAppStatusLiveData().getValue().getExposureDays().size();
- if (((checkinReports > 0 && tracingReports > 0) || checkinReports > 1)) {
+ boolean isReportedPositive = tracingViewModel.getTracingStatusInterface().isReportedAsInfected();
+ if (((checkinReports > 0 && tracingReports > 0) || checkinReports > 1) && !isReportedPositive) {
FragmentExtensionsKt.showFragment(this, ReportsOverviewFragment.newInstance(), true);
} else {
FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), true);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsOverviewFragment.kt
index c0bb1be7f..b91adc596 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsOverviewFragment.kt
@@ -15,6 +15,8 @@ import ch.admin.bag.dp3t.databinding.FragmentReportsOverviewBinding
import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
+import org.crowdnotifier.android.sdk.model.ExposureEvent
+import org.dpppt.android.sdk.models.ExposureDay
class ReportsOverviewFragment : Fragment() {
@@ -29,25 +31,21 @@ class ReportsOverviewFragment : Fragment() {
private val secureStorage by lazy { SecureStorage.getInstance(requireContext()) }
private lateinit var binding: FragmentReportsOverviewBinding
+ private val recyclerAdapter = ReportsRecyclerAdapter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentReportsOverviewBinding.inflate(inflater)
return binding.apply {
reportsToolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
- val adapter = ReportsRecyclerAdapter()
- reportsRecyclerView.adapter = adapter
- crowdNotifierViewModel.exposures.observe(viewLifecycleOwner) { checkinExposures ->
+ reportsRecyclerView.adapter = recyclerAdapter
- adapter.setItems(checkinExposures.map {
- CheckinReportItem(it, diaryStorage.getDiaryEntryWithId(it.id))
- }.toMutableList().apply {
- val tracingExposureDays = tracingViewModel.tracingStatusInterface.exposureDays.reversed()
- if (tracingExposureDays.isNotEmpty()) {
- add(0, ProximityTracingReportItem(tracingExposureDays))
- }
- })
+ crowdNotifierViewModel.exposures.observe(viewLifecycleOwner) {
+ updateRecyclerList(it, tracingViewModel.tracingStatusInterface.exposureDays)
}
- adapter.setOnClickListener {
+ tracingViewModel.tracingStatusLiveData.observe(viewLifecycleOwner) { tracingStatus ->
+ updateRecyclerList(crowdNotifierViewModel.exposures.value ?: listOf(), tracingStatus.exposureDays)
+ }
+ recyclerAdapter.setOnClickListener {
showFragment(ReportsFragment.newInstance(it))
}
if (secureStorage.isReportsHeaderAnimationPending) {
@@ -56,6 +54,20 @@ class ReportsOverviewFragment : Fragment() {
}.root
}
+ private fun updateRecyclerList(checkinExposures: List, tracingExposureDays: List) {
+ val items = checkinExposures.map {
+ CheckinReportItem(it, diaryStorage.getDiaryEntryWithId(it.id))
+ }.toMutableList().apply {
+ if (tracingExposureDays.isNotEmpty()) {
+ add(0, ProximityTracingReportItem(tracingExposureDays))
+ }
+ }
+ if (items.isEmpty()) {
+ parentFragmentManager.popBackStack()
+ }
+ recyclerAdapter.setItems(items)
+ }
+
private fun setupSplashScreen() {
binding.splashReport.apply {
root.isVisible = true
From 6ca8e85e2299b47d08f51b46e774ff14471c200f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Mon, 24 May 2021 19:49:22 +0200
Subject: [PATCH 064/207] update to latest sdk version
---
app/build.gradle | 2 +-
.../main/java/ch/admin/bag/dp3t/contacts/HistoryViewHolder.java | 2 ++
app/src/main/res/values/strings_untranslated.xml | 1 +
3 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 719adb4d4..b3ec44195 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -157,7 +157,7 @@ sonarqube {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
- def dp3t_sdk_version = '2.2.1'
+ def dp3t_sdk_version = '2.3.0-dev-8'
devImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version-calibration"
teschtImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
abnahmeImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
diff --git a/app/src/main/java/ch/admin/bag/dp3t/contacts/HistoryViewHolder.java b/app/src/main/java/ch/admin/bag/dp3t/contacts/HistoryViewHolder.java
index 54e3c1be3..14683dcb7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/contacts/HistoryViewHolder.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/contacts/HistoryViewHolder.java
@@ -52,6 +52,8 @@ private static int getLabelForType(HistoryEntryType type) {
return R.string.synchronizations_view_sync_via_fake_request;
case NEXT_DAY_KEY_UPLOAD_REQUEST:
return R.string.synchronizations_view_sync_via_next_day_key_upload;
+ case NOTIFICATION:
+ return R.string.synchronizations_view_notification;
default:
throw new IllegalArgumentException("Unknown HistoryEntryType: " + type);
}
diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml
index 72ac552a3..d9e7079a2 100644
--- a/app/src/main/res/values/strings_untranslated.xml
+++ b/app/src/main/res/values/strings_untranslated.xml
@@ -15,4 +15,5 @@
" • Scheduled"" • Fake Report"" • Pending Upload"
+ " • Notification"
\ No newline at end of file
From abaa25eade37ea066a9b00e15fe22587e2310c89 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 6 May 2021 09:23:32 +0200
Subject: [PATCH 065/207] Implemented Checkin Sharing Fragment
---
app/build.gradle | 14 ++--
.../networking/UserUploadRepository.kt | 31 ++++++++
.../checkin/networking/UserUploadService.kt | 12 +++
.../bag/dp3t/extensions/FragmentExtensions.kt | 22 +++++-
.../admin/bag/dp3t/inform/CheckinAdapter.kt | 50 ++++++++++++
.../admin/bag/dp3t/inform/InformActivity.java | 6 +-
.../admin/bag/dp3t/inform/InformViewModel.kt | 45 +++++++++++
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 63 +++++++++++++++
.../dp3t/inform/models/NetworkStatusModels.kt | 18 +++++
.../inform/models/SelectableCheckinItem.kt | 5 ++
.../bag/dp3t/util/UserUploadInfoExtensions.kt | 16 ++++
app/src/main/proto/App.proto | 14 ++++
.../res/layout/fragment_share_checkins.xml | 79 +++++++++++++++++++
.../res/layout/item_selectable_checkin.xml | 55 +++++++++++++
14 files changed, 416 insertions(+), 14 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/models/SelectableCheckinItem.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/UserUploadInfoExtensions.kt
create mode 100644 app/src/main/res/layout/fragment_share_checkins.xml
create mode 100644 app/src/main/res/layout/item_selectable_checkin.xml
diff --git a/app/build.gradle b/app/build.gradle
index 83ea09c2d..7e96f1cd6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -70,7 +70,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-d.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-d.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://app-dev-ws.notify-me.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-d.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.dev.CONFIG_CERTIFICATE}\""
@@ -84,7 +84,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-t.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-t.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-t.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://app-dev-ws.notify-me.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-t.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.test.CONFIG_CERTIFICATE}\""
@@ -98,7 +98,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1-a.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://app-dev-ws.notify-me.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-a.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
@@ -112,7 +112,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://app-dev-ws.notify-me.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
@@ -125,7 +125,7 @@ android {
buildConfigField 'String', 'BUCKET_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'REPORT_URL', '"https://www.pt1.bfs.admin.ch/"'
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
- buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.todo.ch"'
+ buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://app-dev-ws.notify-me.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
buildConfigField "String", "TRACE_QR_CODE_PREFIX", '"https://upload-dev.notify-me.ch"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
@@ -207,7 +207,7 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-7'
+ implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-8'
implementation 'androidx.appcompat:appcompat:1.2.0'
@@ -227,7 +227,7 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.gms:play-services-base:17.5.0'
implementation 'com.google.protobuf:protobuf-lite:3.0.1'
-
+ implementation('com.squareup.retrofit2:converter-protobuf:2.9.0') { transitive = false }
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
new file mode 100644
index 000000000..9e405ea20
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
@@ -0,0 +1,31 @@
+package ch.admin.bag.dp3t.checkin.networking
+
+import ch.admin.bag.dp3t.BuildConfig
+import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
+import okhttp3.OkHttpClient
+import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.backend.UserAgentInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.protobuf.ProtoConverterFactory
+
+class UserUploadRepository {
+
+ private var userUploadService: UserUploadService
+
+ init {
+ val okHttpBuilder = OkHttpClient.Builder()
+
+ okHttpBuilder.addInterceptor(UserAgentInterceptor(DP3T.getUserAgent()))
+
+ val retrofit = Retrofit.Builder()
+ .baseUrl(BuildConfig.PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL)
+ .client(okHttpBuilder.build())
+ .addConverterFactory(ProtoConverterFactory.create())
+ .build()
+ userUploadService = retrofit.create(UserUploadService::class.java)
+ }
+
+ suspend fun userUpload(userUploadPayload: UserUploadPayload) = userUploadService.userUpload(userUploadPayload)
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
new file mode 100644
index 000000000..272722ab7
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
@@ -0,0 +1,12 @@
+package ch.admin.bag.dp3t.checkin.networking
+
+import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+interface UserUploadService {
+
+ @POST("v3/userupload")
+ suspend fun userUpload(@Body userUploadPayload: UserUploadPayload)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
index ce5d4b094..9025583c8 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
@@ -1,12 +1,26 @@
package ch.admin.bag.dp3t.extensions
+import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import ch.admin.bag.dp3t.R
-fun Fragment.showFragment(fragment: Fragment, addToBackStack: Boolean = true) {
+fun Fragment.showFragment(
+ fragment: Fragment,
+ @IdRes container: Int = R.id.main_fragment_container,
+ addToBackStack: Boolean = true,
+ modalAnimation: Boolean = false
+) {
requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, fragment)
- .apply { if (addToBackStack) addToBackStack(fragment::class.java.canonicalName) }
+ .apply {
+ if (modalAnimation) {
+ this.setCustomAnimations(
+ R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit
+ )
+ } else {
+ this.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ }
+ }
+ .replace(container, fragment)
+ .apply { if (addToBackStack) this.addToBackStack(fragment::class.java.canonicalName) }
.commit()
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
new file mode 100644
index 000000000..b09b0b84e
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
@@ -0,0 +1,50 @@
+package ch.admin.bag.dp3t.inform
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
+import ch.admin.bag.dp3t.databinding.ItemSelectableCheckinBinding
+import ch.admin.bag.dp3t.inform.models.SelectableCheckinItem
+
+class CheckinAdapter : RecyclerView.Adapter() {
+
+ private var checkins = mutableListOf()
+ private var itemSelectionListener: ((selectedItem: DiaryEntry, selected: Boolean) -> Unit)? = null
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheckinViewHolder {
+ return CheckinViewHolder(ItemSelectableCheckinBinding.inflate(LayoutInflater.from(parent.context), parent, false))
+ }
+
+ override fun onBindViewHolder(holderCheckin: CheckinViewHolder, position: Int) {
+ holderCheckin.bind(checkins[position])
+ }
+
+ override fun getItemCount() = checkins.size
+
+ inner class CheckinViewHolder(private val binding: ItemSelectableCheckinBinding) : RecyclerView.ViewHolder(binding.root) {
+ fun bind(selectableCheckinItem: SelectableCheckinItem) {
+ binding.apply {
+ checkinTitle.text = selectableCheckinItem.diaryEntry.venueInfo.title
+ checkinDetail1.text = "TODO"
+ checkinDetail2.text = "TODO"
+ checkbox.isChecked = selectableCheckinItem.isSelected
+ checkbox.setOnCheckedChangeListener { _, isChecked ->
+ selectableCheckinItem.isSelected = isChecked
+ itemSelectionListener?.invoke(selectableCheckinItem.diaryEntry, isChecked)
+ }
+ }
+ }
+ }
+
+ fun setData(selectableCheckinItems: List) {
+ checkins.clear()
+ checkins.addAll(selectableCheckinItems)
+ notifyDataSetChanged()
+ }
+
+ fun itemSelectionListener(listener: (selectedItem: DiaryEntry, selected: Boolean) -> Unit) {
+ itemSelectionListener = listener
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformActivity.java b/app/src/main/java/ch/admin/bag/dp3t/inform/InformActivity.java
index 241454e21..e0d87bfce 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformActivity.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformActivity.java
@@ -20,7 +20,7 @@
public class InformActivity extends FragmentActivity {
- private boolean allowed = true;
+ private boolean backpressAllowed = true;
public static final String EXTRA_COVIDCODE = "EXTRA_COVIDCODE";
@Override
@@ -38,13 +38,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
@Override
public void onBackPressed() {
- if (allowed) {
+ if (backpressAllowed) {
super.onBackPressed();
}
}
public void allowBackButton(boolean allowed) {
- this.allowed = allowed;
+ this.backpressAllowed = allowed;
}
@Override
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
new file mode 100644
index 000000000..d335f9ae5
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -0,0 +1,45 @@
+package ch.admin.bag.dp3t.inform
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.liveData
+import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
+import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
+import ch.admin.bag.dp3t.inform.models.Resource
+import ch.admin.bag.dp3t.inform.models.SelectableCheckinItem
+import ch.admin.bag.dp3t.util.toUploadVenueInfo
+import kotlinx.coroutines.Dispatchers
+import org.crowdnotifier.android.sdk.CrowdNotifier
+
+private const val USER_UPLOAD_VERSION = 3
+
+class InformViewModel(application: Application) : AndroidViewModel(application) {
+
+ private val userUploadRepository = UserUploadRepository()
+ private val diaryStorage = DiaryStorage.getInstance(application)
+
+ val selectableDiaryItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
+
+ fun userUpload() = liveData(Dispatchers.IO) {
+ emit(Resource.loading(data = null))
+ try {
+ emit(Resource.success(data = userUploadRepository.userUpload(getUserUploadPayload())))
+ } catch (exception: Exception) {
+ emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
+ }
+ }
+
+ private fun getUserUploadPayload(): UserUploadPayload {
+ val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
+ selectableDiaryItems.filter {
+ it.isSelected
+ }.map {
+ CrowdNotifier.generateUserUploadInfo(it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime)
+ }.flatten().forEach {
+ userUploadPayloadBuilder.addVenueInfos(it.toUploadVenueInfo())
+ }
+ return userUploadPayloadBuilder.build()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
new file mode 100644
index 000000000..ffa9a0d43
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -0,0 +1,63 @@
+package ch.admin.bag.dp3t.inform
+
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.databinding.FragmentShareCheckinsBinding
+import ch.admin.bag.dp3t.inform.models.Status
+import ch.admin.bag.dp3t.util.showFragment
+
+class ShareCheckinsFragment : Fragment() {
+
+ companion object {
+ @JvmStatic
+ fun newInstance() = ShareCheckinsFragment()
+ }
+
+ private val informViewModel: InformViewModel by activityViewModels()
+
+ private lateinit var binding: FragmentShareCheckinsBinding
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ binding = FragmentShareCheckinsBinding.inflate(inflater).apply {
+ val adapter = CheckinAdapter()
+ checkinsRecyclerView.adapter = adapter
+ checkinsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
+ adapter.setData(informViewModel.selectableDiaryItems)
+ adapter.itemSelectionListener { selectedItem, selected ->
+ informViewModel.selectableDiaryItems.find { it.diaryEntry == selectedItem }?.isSelected = selected
+ }
+ selectAllCheckbox.setOnCheckedChangeListener { _, isChecked ->
+ informViewModel.selectableDiaryItems.forEach { it.isSelected = isChecked }
+ adapter.setData(informViewModel.selectableDiaryItems)
+ }
+ dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
+ dontSendButton.setOnClickListener {
+
+ }
+ sendButton.setOnClickListener {
+ informViewModel.userUpload().observe(viewLifecycleOwner) {
+ when (it.status) {
+ Status.LOADING -> {
+ //TODO: Show loading animation
+ }
+ Status.ERROR -> {
+ //TODO: Handle error
+ }
+ Status.SUCCESS -> {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ }
+ }
+ }
+ }
+ }
+ return binding.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
new file mode 100644
index 000000000..a8248dc51
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
@@ -0,0 +1,18 @@
+package ch.admin.bag.dp3t.inform.models
+
+enum class Status {
+ SUCCESS,
+ ERROR,
+ LOADING
+}
+
+data class Resource(val status: Status, val data: T?, val message: String?) {
+ companion object {
+ fun success(data: T): Resource = Resource(status = Status.SUCCESS, data = data, message = null)
+
+ fun error(data: T?, message: String): Resource =
+ Resource(status = Status.ERROR, data = data, message = message)
+
+ fun loading(data: T?): Resource = Resource(status = Status.LOADING, data = data, message = null)
+ }
+}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/models/SelectableCheckinItem.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/models/SelectableCheckinItem.kt
new file mode 100644
index 000000000..a12ae1d14
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/models/SelectableCheckinItem.kt
@@ -0,0 +1,5 @@
+package ch.admin.bag.dp3t.inform.models
+
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
+
+data class SelectableCheckinItem(val diaryEntry: DiaryEntry, var isSelected: Boolean)
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/UserUploadInfoExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/util/UserUploadInfoExtensions.kt
new file mode 100644
index 000000000..87e708b0f
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/UserUploadInfoExtensions.kt
@@ -0,0 +1,16 @@
+package ch.admin.bag.dp3t.util
+
+import ch.admin.bag.dp3t.checkin.models.UploadVenueInfo
+import com.google.protobuf.ByteString
+import org.crowdnotifier.android.sdk.model.UserUploadInfo
+
+fun UserUploadInfo.toUploadVenueInfo(): UploadVenueInfo {
+ return UploadVenueInfo.newBuilder()
+ .setPreId(ByteString.copyFrom(preId))
+ .setTimeKey(ByteString.copyFrom(timeKey))
+ .setNotificationKey(ByteString.copyFrom(notificationKey))
+ .setIntervalStartMs(intervalStartMs)
+ .setIntervalEndMs(intervalEndMs)
+ .setFake(false)
+ .build()
+}
\ No newline at end of file
diff --git a/app/src/main/proto/App.proto b/app/src/main/proto/App.proto
index 57b19ad8a..995364bd4 100644
--- a/app/src/main/proto/App.proto
+++ b/app/src/main/proto/App.proto
@@ -47,3 +47,17 @@ message AssociatedData {
int64 endTimestamp = 4;
bytes countryData = 5;
}
+
+message UserUploadPayload {
+ int32 version = 1;
+ repeated UploadVenueInfo venueInfos = 2;
+}
+message UploadVenueInfo {
+ bytes preId = 1;
+ bytes timeKey = 2;
+ int64 intervalStartMs = 3;
+ int64 intervalEndMs = 4;
+ bytes notificationKey = 5;
+ bool fake = 6;
+}
+
diff --git a/app/src/main/res/layout/fragment_share_checkins.xml b/app/src/main/res/layout/fragment_share_checkins.xml
new file mode 100644
index 000000000..fd7dfab97
--- /dev/null
+++ b/app/src/main/res/layout/fragment_share_checkins.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_selectable_checkin.xml b/app/src/main/res/layout/item_selectable_checkin.xml
new file mode 100644
index 000000000..df4f26910
--- /dev/null
+++ b/app/src/main/res/layout/item_selectable_checkin.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 568b8cc699fa0f92a32bb4884e63a0900e4cbc26 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 6 May 2021 14:27:28 +0200
Subject: [PATCH 066/207] Move Network Requests from Inform Fragment to
InformViewModel
---
.../admin/bag/dp3t/inform/InformFragment.java | 301 ------------------
.../admin/bag/dp3t/inform/InformFragment.kt | 217 +++++++++++++
.../admin/bag/dp3t/inform/InformViewModel.kt | 115 ++++++-
.../dp3t/inform/models/NetworkStatusModels.kt | 10 +-
.../bag/dp3t/networking/AuthCodeRepository.kt | 38 +--
.../bag/dp3t/networking/AuthCodeService.kt | 8 +-
.../admin/bag/dp3t/networking/FakeWorker.kt | 2 +-
.../AuthenticationCodeResponseModelV2.kt | 6 +
.../admin/bag/dp3t/storage/SecureStorage.java | 13 +-
app/src/main/res/layout/fragment_inform.xml | 4 +-
10 files changed, 367 insertions(+), 347 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.java b/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.java
deleted file mode 100644
index 2762d1c83..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.inform;
-
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
-
-import java.util.Date;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.TimeUnit;
-
-import com.google.android.gms.common.api.ApiException;
-
-import org.dpppt.android.sdk.DP3T;
-import org.dpppt.android.sdk.backend.ResponseCallback;
-import org.dpppt.android.sdk.internal.logger.Logger;
-import org.dpppt.android.sdk.models.DayDate;
-import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization;
-
-import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.inform.views.ChainedEditText;
-import ch.admin.bag.dp3t.networking.AuthCodeRepository;
-import ch.admin.bag.dp3t.networking.errors.InvalidCodeError;
-import ch.admin.bag.dp3t.networking.errors.ResponseError;
-import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel;
-import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModel;
-import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.util.ENExceptionHelper;
-import ch.admin.bag.dp3t.util.JwtUtil;
-import ch.admin.bag.dp3t.util.PhoneUtil;
-import ch.admin.bag.dp3t.viewmodel.TracingViewModel;
-
-import static ch.admin.bag.dp3t.inform.InformActivity.EXTRA_COVIDCODE;
-
-public class InformFragment extends Fragment {
-
- private static final String TAG = "InformFragment";
-
- private static final long TIMEOUT_VALID_CODE = 1000L * 60 * 5;
- private final long MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L;
-
- private static final String REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}";
-
- private ChainedEditText authCodeInput;
- private AlertDialog progressDialog;
- private Button buttonSend;
-
- private SecureStorage secureStorage;
- private TracingViewModel tracingViewModel;
-
- public static InformFragment newInstance() {
- return new InformFragment();
- }
-
- public InformFragment() {
- super(R.layout.fragment_inform);
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- secureStorage = SecureStorage.getInstance(getContext());
- tracingViewModel = new ViewModelProvider(requireActivity()).get(TracingViewModel.class);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- ((InformActivity) requireActivity()).allowBackButton(true);
- buttonSend = view.findViewById(R.id.trigger_fragment_button_trigger);
- authCodeInput = view.findViewById(R.id.trigger_fragment_input);
- authCodeInput.addTextChangedListener(new ChainedEditText.ChainedEditTextListener() {
- @Override
- public void onTextChanged(String input) {
- buttonSend.setEnabled(input.matches(REGEX_CODE_PATTERN));
- }
-
- @Override
- public void onEditorSendAction() {
- if (buttonSend.isEnabled()) buttonSend.callOnClick();
- }
- });
-
- long lastRequestTime = secureStorage.getLastInformRequestTime();
- String lastCode = secureStorage.getLastInformCode();
- String lastToken = secureStorage.getLastInformToken();
-
- if (System.currentTimeMillis() - lastRequestTime < TIMEOUT_VALID_CODE) {
- authCodeInput.setText(lastCode);
- } else if (lastCode != null || lastToken != null) {
- secureStorage.clearInformTimeAndCodeAndToken();
- }
-
- if (requireActivity().getIntent().getExtras() != null) {
- String covidCode = requireActivity().getIntent().getExtras().getString(EXTRA_COVIDCODE);
- if (covidCode != null) {
- authCodeInput.setText(covidCode);
- }
- }
-
- buttonSend.setOnClickListener(v -> {
- buttonSend.setEnabled(false);
- setInvalidCodeErrorVisible(false);
- String authCode = authCodeInput.getText();
-
- boolean isTracingEnabled = DP3T.isTracingEnabled(requireContext());
- if (isTracingEnabled) {
- authenticateInputOrInformExposed(authCode);
- } else {
- askUserToEnableTracing(authCode);
- }
- });
-
- view.findViewById(R.id.cancel_button).setOnClickListener(v -> getActivity().finish());
-
- view.findViewById(R.id.inform_invalid_code_error).setOnClickListener(v -> PhoneUtil.callAppHotline(v.getContext()));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- authCodeInput.requestFocus();
- }
-
- private void authenticateInputOrInformExposed(String authCode) {
- long lastTimestamp = secureStorage.getLastInformRequestTime();
- String lastAuthToken = secureStorage.getLastInformToken();
-
- progressDialog = createProgressDialog();
- if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && lastAuthToken != null) {
- Date onsetDate = JwtUtil.getOnsetDate(lastAuthToken);
- informExposed(onsetDate, getAuthorizationHeader(lastAuthToken));
- } else {
- authenticateInput(authCode);
- }
- }
-
- private void askUserToEnableTracing(String authCode) {
- new AlertDialog.Builder(getContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(R.string.android_inform_tracing_enabled_explanation)
- .setOnCancelListener(dialog -> buttonSend.setEnabled(true))
- .setNegativeButton(R.string.cancel, (dialog, which) -> buttonSend.setEnabled(true))
- .setPositiveButton(R.string.activate_tracing_button, (dialog, which) -> enableTracing(authCode))
- .show();
- }
-
- private void enableTracing(String authCode) {
- tracingViewModel.enableTracing(getActivity(),
- () -> authenticateInputOrInformExposed(authCode),
- (e) -> {
- String message = ENExceptionHelper.getErrorMessage(e, getActivity());
- Logger.e(TAG, message);
- new AlertDialog.Builder(getActivity(), R.style.NextStep_AlertDialogStyle)
- .setTitle(R.string.android_en_start_failure)
- .setMessage(message)
- .setOnDismissListener(dialog -> buttonSend.setEnabled(true))
- .setPositiveButton(R.string.android_button_ok, (dialog, which) -> {})
- .show();
- },
- () -> buttonSend.setEnabled(true));
- }
-
- private void authenticateInput(String authCode) {
- AuthCodeRepository authCodeRepository = new AuthCodeRepository(getContext());
- authCodeRepository.getAccessToken(new AuthenticationCodeRequestModel(authCode, 0),
- new ResponseCallback() {
- @Override
- public void onSuccess(AuthenticationCodeResponseModel response) {
- String accessToken = response.getAccessToken();
-
- secureStorage.saveInformTimeAndCodeAndToken(authCode, accessToken);
-
- Date onsetDate = JwtUtil.getOnsetDate(accessToken);
- if (onsetDate == null) {
- showErrorDialog(InformRequestError.BLACK_INVALID_AUTH_RESPONSE_FORM);
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- buttonSend.setEnabled(true);
- return;
- }
- informExposed(onsetDate, getAuthorizationHeader(accessToken));
- }
-
- @Override
- public void onError(Throwable throwable) {
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- if (throwable instanceof InvalidCodeError) {
- setInvalidCodeErrorVisible(true);
- return;
- } else if (throwable instanceof ResponseError) {
- showErrorDialog(InformRequestError.BLACK_STATUS_ERROR,
- String.valueOf(((ResponseError) throwable).getStatusCode()));
- } else {
- showErrorDialog(InformRequestError.BLACK_MISC_NETWORK_ERROR);
- }
- buttonSend.setEnabled(true);
- }
- },
- getViewLifecycleOwner());
- }
-
- private void informExposed(Date onsetDate, String authorizationHeader) {
- DP3T.sendIAmInfected(getActivity(), onsetDate,
- new ExposeeAuthMethodAuthorization(authorizationHeader), new ResponseCallback() {
- @Override
- public void onSuccess(DayDate oldestSharedKeyDayDate) {
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- secureStorage.clearInformTimeAndCodeAndToken();
-
- // Store the oldest shared Key date of this report (but at least now-MAX_EXPOSURE_AGE_MILLIS)
- long oldestSharedKeyDate = Math.max(oldestSharedKeyDayDate.getStartOfDayTimestamp(),
- System.currentTimeMillis() - MAX_EXPOSURE_AGE_MILLIS);
- secureStorage.setPositiveReportOldestSharedKey(oldestSharedKeyDate);
-
- // Ask if user wants to end isolation after 14 days
- long isolationEndDialogTimestamp = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14);
- secureStorage.setIsolationEndDialogTimestamp(isolationEndDialogTimestamp);
-
- getParentFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter,
- R.anim.slide_pop_exit)
- .replace(R.id.inform_fragment_container, ThankYouFragment.newInstance())
- .commit();
- }
-
- @Override
- public void onError(Throwable throwable) {
- if (progressDialog != null && progressDialog.isShowing()) {
- progressDialog.dismiss();
- }
- if (throwable instanceof ResponseError) {
- showErrorDialog(InformRequestError.RED_STATUS_ERROR,
- String.valueOf(((ResponseError) throwable).getStatusCode()));
- } else if (throwable instanceof CancellationException) {
- showErrorDialog(InformRequestError.RED_USER_CANCELLED_SHARE);
- } else if (throwable instanceof ApiException) {
- showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR,
- String.valueOf(((ApiException) throwable).getStatusCode()));
- } else {
- showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR);
- }
- throwable.printStackTrace();
- buttonSend.setEnabled(true);
- }
- });
- }
-
- private void setInvalidCodeErrorVisible(boolean visible) {
- getView().findViewById(R.id.inform_invalid_code_error).setVisibility(visible ? View.VISIBLE : View.GONE);
- getView().findViewById(R.id.inform_input_text).setVisibility(visible ? View.GONE : View.VISIBLE);
- }
-
- private AlertDialog createProgressDialog() {
- return new AlertDialog.Builder(getContext())
- .setView(R.layout.dialog_loading)
- .show();
- }
-
- private void showErrorDialog(InformRequestError error) {
- showErrorDialog(error, null);
- }
-
- private void showErrorDialog(InformRequestError error, @Nullable String addErrorCode) {
- AlertDialog.Builder errorDialogBuilder = new AlertDialog.Builder(getContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(error.getErrorMessage())
- .setPositiveButton(R.string.android_button_ok, (dialog, which) -> {});
- String errorCode = error.getErrorCode(addErrorCode);
- TextView errorCodeView =
- (TextView) getLayoutInflater().inflate(R.layout.view_dialog_error_code, (ViewGroup) getView(), false);
- errorCodeView.setText(errorCode);
- errorDialogBuilder.setView(errorCodeView);
- errorDialogBuilder.show();
- }
-
- private String getAuthorizationHeader(String accessToken) {
- return "Bearer " + accessToken;
- }
-
-}
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
new file mode 100644
index 000000000..3e1fbc3dc
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformFragment.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2020 Ubique Innovation AG
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package ch.admin.bag.dp3t.inform
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+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.databinding.FragmentInformBinding
+import ch.admin.bag.dp3t.inform.models.Status
+import ch.admin.bag.dp3t.inform.views.ChainedEditText
+import ch.admin.bag.dp3t.inform.views.ChainedEditText.ChainedEditTextListener
+import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
+import ch.admin.bag.dp3t.networking.errors.ResponseError
+import ch.admin.bag.dp3t.util.ENExceptionHelper
+import ch.admin.bag.dp3t.util.PhoneUtil
+import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.viewmodel.TracingViewModel
+import com.google.android.gms.common.api.ApiException
+import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.internal.logger.Logger
+import java.util.concurrent.CancellationException
+
+private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}"
+
+class InformFragment : Fragment() {
+
+ private val tracingViewModel: TracingViewModel by activityViewModels()
+ private val informViewModel: InformViewModel by activityViewModels()
+ private lateinit var progressDialog: AlertDialog
+
+ private lateinit var binding: FragmentInformBinding
+
+ companion object {
+ private const val TAG = "InformFragment"
+
+ @JvmStatic
+ fun newInstance() = InformFragment()
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ binding = FragmentInformBinding.inflate(inflater).apply {
+ (requireActivity() as InformActivity).allowBackButton(true)
+ progressDialog = AlertDialog.Builder(requireContext()).setView(R.layout.dialog_loading).create()
+ covidcodeInput.addTextChangedListener(object : ChainedEditTextListener {
+ override fun onTextChanged(input: String) {
+ sendButton.isEnabled = input.matches(REGEX_CODE_PATTERN.toRegex())
+ }
+
+ override fun onEditorSendAction() {
+ if (sendButton.isEnabled) sendButton.callOnClick()
+ }
+ })
+
+ informViewModel.getLastAuthCode()?.let {
+ covidcodeInput.text = it
+ }
+
+ if (requireActivity().intent.extras != null) {
+ val covidCode = requireActivity().intent.extras?.getString(InformActivity.EXTRA_COVIDCODE)
+ if (covidCode != null) {
+ covidcodeInput.text = covidCode
+ }
+ }
+ sendButton.setOnClickListener {
+ sendButton.isEnabled = false
+ setInvalidCodeErrorVisible(false)
+ val authCode = covidcodeInput.text
+ val isTracingEnabled = DP3T.isTracingEnabled(requireContext())
+ if (isTracingEnabled) {
+ authenticateInputAndInformExposed(authCode)
+ } else {
+ askUserToEnableTracing(authCode)
+ }
+ }
+ cancelButton.setOnClickListener { requireActivity().finish() }
+ informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
+ }
+
+ return binding.root
+ }
+
+
+ override fun onResume() {
+ super.onResume()
+ binding.covidcodeInput.requestFocus()
+ }
+
+ private fun authenticateInputAndInformExposed(authCode: String) {
+ informViewModel.authenticateInputAndGetDP3TAccessToken(authCode).observe(viewLifecycleOwner) {
+ when (it.status) {
+ Status.LOADING -> {
+ setProgressDialogVisible(true)
+ binding.sendButton.isEnabled = false
+ }
+ Status.ERROR -> {
+ setProgressDialogVisible(false)
+ it.exception?.let { exception ->
+ handleAuthenticateRequestError(exception)
+ }
+ binding.sendButton.isEnabled = true
+ }
+ Status.SUCCESS -> {
+ it.data?.let { accessToken -> informExposed(accessToken) }
+ }
+ }
+ }
+ }
+
+ private fun handleAuthenticateRequestError(throwable: Throwable) {
+ when (throwable) {
+ is InvalidCodeError -> setInvalidCodeErrorVisible(true)
+ is ResponseError -> showErrorDialog(InformRequestError.BLACK_STATUS_ERROR, throwable.statusCode.toString())
+ else -> showErrorDialog(InformRequestError.BLACK_MISC_NETWORK_ERROR)
+ }
+ throwable.printStackTrace()
+ }
+
+ private fun informExposed(accessToken: String) {
+ informViewModel.informExposed(accessToken, requireActivity()).observe(viewLifecycleOwner) {
+ when (it.status) {
+ Status.LOADING -> {
+ setProgressDialogVisible(true)
+ binding.sendButton.isEnabled = false
+ }
+ Status.ERROR -> {
+ setProgressDialogVisible(false)
+ it.exception?.let { exception ->
+ handleInformExposedRequestError(exception)
+ }
+ binding.sendButton.isEnabled = true
+ }
+ Status.SUCCESS -> {
+ setProgressDialogVisible(false)
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ binding.sendButton.isEnabled = true
+ }
+ }
+ }
+ }
+
+ private fun handleInformExposedRequestError(throwable: Throwable) {
+ when (throwable) {
+ is ResponseError -> showErrorDialog(InformRequestError.RED_STATUS_ERROR, throwable.statusCode.toString())
+ is CancellationException -> showErrorDialog(InformRequestError.RED_USER_CANCELLED_SHARE)
+ is ApiException -> showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR, throwable.statusCode.toString())
+ else -> showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR)
+ }
+ throwable.printStackTrace()
+ }
+
+ private fun setProgressDialogVisible(isVisible: Boolean) {
+ if (isVisible && !progressDialog.isShowing) {
+ progressDialog.show()
+ }
+ if (!isVisible && progressDialog.isShowing) {
+ progressDialog.dismiss()
+ }
+ }
+
+ private fun askUserToEnableTracing(authCode: String) {
+ AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.android_inform_tracing_enabled_explanation)
+ .setOnCancelListener { binding.sendButton.isEnabled = true }
+ .setNegativeButton(R.string.cancel) { _, _ -> binding.sendButton.isEnabled = true }
+ .setPositiveButton(R.string.activate_tracing_button) { _, _ -> enableTracing(authCode) }
+ .show()
+ }
+
+ private fun enableTracing(authCode: String) {
+ tracingViewModel.enableTracing(requireActivity(),
+ { authenticateInputAndInformExposed(authCode) },
+ { e: Exception? ->
+ val message = ENExceptionHelper.getErrorMessage(e, activity)
+ Logger.e(TAG, message)
+ AlertDialog.Builder(requireActivity(), R.style.NextStep_AlertDialogStyle)
+ .setTitle(R.string.android_en_start_failure)
+ .setMessage(message)
+ .setOnDismissListener { binding.sendButton.isEnabled = true }
+ .setPositiveButton(R.string.android_button_ok) { _, _ -> }
+ .show()
+ }
+ ) { binding.sendButton.isEnabled = true }
+ }
+
+ private fun setInvalidCodeErrorVisible(visible: Boolean) {
+ binding.apply {
+ informInvalidCodeError.isVisible = visible
+ informInputText.isVisible = visible
+ }
+ }
+
+ private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
+ val errorDialogBuilder = AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(error.errorMessage)
+ .setPositiveButton(R.string.android_button_ok) { _, _ -> }
+ val errorCode = error.getErrorCode(addErrorCode)
+ val errorCodeView = layoutInflater.inflate(R.layout.view_dialog_error_code, view as ViewGroup?, false) as TextView
+ errorCodeView.text = errorCode
+ errorDialogBuilder.setView(errorCodeView)
+ errorDialogBuilder.show()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index d335f9ae5..61a50d759 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -1,5 +1,6 @@
package ch.admin.bag.dp3t.inform
+import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.liveData
@@ -8,17 +9,34 @@ import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.inform.models.Resource
import ch.admin.bag.dp3t.inform.models.SelectableCheckinItem
+import ch.admin.bag.dp3t.networking.AuthCodeRepository
+import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
+import ch.admin.bag.dp3t.storage.SecureStorage
+import ch.admin.bag.dp3t.util.JwtUtil
import ch.admin.bag.dp3t.util.toUploadVenueInfo
import kotlinx.coroutines.Dispatchers
import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.backend.ResponseCallback
+import org.dpppt.android.sdk.models.DayDate
+import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization
+import java.util.concurrent.TimeUnit
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
private const val USER_UPLOAD_VERSION = 3
+private const val TIMEOUT_VALID_CODE = 1000L * 60 * 5
+private const val MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L
+private const val ISOLATION_DURATION_DAYS = 14L
class InformViewModel(application: Application) : AndroidViewModel(application) {
+ private val authCodeRepository = AuthCodeRepository(application)
private val userUploadRepository = UserUploadRepository()
private val diaryStorage = DiaryStorage.getInstance(application)
+ private val secureStorage = SecureStorage.getInstance(application)
+ //TODO: Persist these Items for DKA cases
val selectableDiaryItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
fun userUpload() = liveData(Dispatchers.IO) {
@@ -26,10 +44,101 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
try {
emit(Resource.success(data = userUploadRepository.userUpload(getUserUploadPayload())))
} catch (exception: Exception) {
- emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
+ emit(Resource.error(data = null, exception = exception))
}
}
+ fun getLastAuthCode(): String? {
+ val lastCode = secureStorage.lastInformCode
+ val lastToken = secureStorage.lastDP3TInformToken
+
+ if (System.currentTimeMillis() - secureStorage.lastInformRequestTime < TIMEOUT_VALID_CODE) {
+ return lastCode
+ } else if (lastCode != null || lastToken != null) {
+ secureStorage.clearInformTimeAndCodeAndToken()
+ }
+ return null
+ }
+
+ fun authenticateInputAndGetDP3TAccessToken(authCode: String) = liveData(Dispatchers.IO) {
+ emit(Resource.loading(data = null))
+ val lastTimestamp = secureStorage.lastInformRequestTime
+ val lastDP3TAuthToken = secureStorage.lastDP3TInformToken
+ if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && lastDP3TAuthToken != null) {
+ emit(Resource.success(lastDP3TAuthToken))
+ } else {
+ try {
+ val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
+ secureStorage.saveInformTimeAndCodeAndToken(
+ authCode,
+ accessTokens.dp3TAccessToken.accessToken,
+ accessTokens.checkInAccessToken.accessToken
+ )
+ emit(Resource.success(data = accessTokens.dp3TAccessToken.accessToken))
+ } catch (exception: Exception) {
+ emit(Resource.error(data = null, exception = exception))
+ }
+ }
+ }
+
+ fun authenticateInputAndGetCheckinAccessToken(authCode: String) = liveData(Dispatchers.IO) {
+ emit(Resource.loading(data = null))
+ val lastTimestamp = secureStorage.lastInformRequestTime
+ val lastCheckinAuthToken = secureStorage.lastCheckinInformToken
+ if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && lastCheckinAuthToken != null) {
+ emit(Resource.success(lastCheckinAuthToken))
+ } else {
+ try {
+ val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
+ secureStorage.saveInformTimeAndCodeAndToken(
+ authCode,
+ accessTokens.dp3TAccessToken.accessToken,
+ accessTokens.checkInAccessToken.accessToken
+ )
+ emit(Resource.success(data = accessTokens.checkInAccessToken.accessToken))
+ } catch (exception: Exception) {
+ emit(Resource.error(data = null, exception = exception))
+ }
+ }
+ }
+
+ fun informExposed(accessToken: String, activity: Activity) = liveData(Dispatchers.IO) {
+ emit(Resource.loading(data = null))
+ val authorizationHeader = getAuthorizationHeader(accessToken)
+ val onsetDate = JwtUtil.getOnsetDate(accessToken)
+
+ var result: Resource? = null
+
+ // Wrapping traditional callback in a suspendCoroutine
+ suspendCoroutine { continuation ->
+ DP3T.sendIAmInfected(activity, onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
+ object : ResponseCallback {
+ override fun onSuccess(oldestSharedKeyDayDate: DayDate) {
+
+ // Store the oldest shared Key date of this report (but at least now-MAX_EXPOSURE_AGE_MILLIS)
+ val oldestSharedKeyDate = Math.max(
+ oldestSharedKeyDayDate.startOfDayTimestamp,
+ System.currentTimeMillis() - MAX_EXPOSURE_AGE_MILLIS
+ )
+ secureStorage.positiveReportOldestSharedKey = oldestSharedKeyDate
+
+ // Ask if user wants to end isolation after 14 days
+ val isolationEndDialogTimestamp =
+ System.currentTimeMillis() + TimeUnit.DAYS.toMillis(ISOLATION_DURATION_DAYS)
+ secureStorage.isolationEndDialogTimestamp = isolationEndDialogTimestamp
+ result = Resource.success(Unit)
+ continuation.resume(Unit)
+ }
+
+ override fun onError(throwable: Throwable) {
+ result = Resource.error(null, throwable)
+ continuation.resume(Unit)
+ }
+ })
+ }
+ result?.let { emit(it) }
+ }
+
private fun getUserUploadPayload(): UserUploadPayload {
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
selectableDiaryItems.filter {
@@ -42,4 +151,8 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
return userUploadPayloadBuilder.build()
}
+ private fun getAuthorizationHeader(accessToken: String): String {
+ return "Bearer $accessToken"
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
index a8248dc51..bffb8f1f5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/models/NetworkStatusModels.kt
@@ -6,13 +6,13 @@ enum class Status {
LOADING
}
-data class Resource(val status: Status, val data: T?, val message: String?) {
+data class Resource(val status: Status, val data: T?, val exception: Throwable?) {
companion object {
- fun success(data: T): Resource = Resource(status = Status.SUCCESS, data = data, message = null)
+ fun success(data: T): Resource = Resource(status = Status.SUCCESS, data = data, exception = null)
- fun error(data: T?, message: String): Resource =
- Resource(status = Status.ERROR, data = data, message = message)
+ fun error(data: T?, exception: Throwable): Resource =
+ Resource(status = Status.ERROR, data = data, exception = exception)
- fun loading(data: T?): Resource = Resource(status = Status.LOADING, data = data, message = null)
+ fun loading(data: T?): Resource = Resource(status = Status.LOADING, data = data, exception = null)
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
index 3720e16a8..8976773ce 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
@@ -17,6 +17,7 @@ import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModel
+import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModelV2
import kotlinx.coroutines.*
import okhttp3.Cache
import okhttp3.Interceptor
@@ -61,43 +62,14 @@ class AuthCodeRepository(context: Context) {
authCodeService = retrofit.create(AuthCodeService::class.java)
}
- fun getAccessToken(
- authenticationCode: AuthenticationCodeRequestModel,
- callbackListener: ResponseCallback,
- lifecycleOwner: LifecycleOwner
- ) {
- lifecycleOwner.lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- try {
- val response = authCodeService.getAccessToken(authenticationCode)
- if (response.isSuccessful) {
- withContext(Dispatchers.Main) {
- callbackListener.onSuccess(response.body())
- }
- } else {
- withContext(Dispatchers.Main) {
- if (response.code() == 404) {
- callbackListener.onError(InvalidCodeError())
- } else {
- callbackListener.onError(ResponseError(response.raw()))
- }
- }
- }
- } catch (e: Exception) {
- withContext(Dispatchers.Main) {
- Logger.e(TAG, "getAccessToken", e)
- callbackListener.onError(e)
- }
- }
- }
- }
- }
@Throws(IOException::class, ResponseError::class)
- suspend fun getAccessTokenSync(authenticationCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModel =
+ suspend fun getAccessTokenSyncV1(authenticationCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModel =
withContext(Dispatchers.IO) {
- val response = authCodeService.getAccessToken(authenticationCode)
+ val response = authCodeService.getAccessTokenV1(authenticationCode)
if (!response.isSuccessful) throw ResponseError(response.raw())
return@withContext response.body() ?: throw ResponseError(response.raw())
}
+
+ suspend fun getAccessToken(authCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2 = authCodeService.getAccessToken(authCode)
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
index 28fa2af1d..5b206848c 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
@@ -11,6 +11,7 @@ package ch.admin.bag.dp3t.networking
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModel
+import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModelV2
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
@@ -19,5 +20,10 @@ import retrofit2.http.POST
interface AuthCodeService {
@Headers("accept: */*", "content-type: application/json")
@POST("v1/onset")
- suspend fun getAccessToken(@Body code: AuthenticationCodeRequestModel): Response
+ suspend fun getAccessTokenV1(@Body code: AuthenticationCodeRequestModel): Response
+
+ @Headers("accept: */*", "content-type: application/json")
+ @POST("v2/onset")
+ suspend fun getAccessToken(@Body code: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
index 475c05488..f8321b80a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
@@ -145,7 +145,7 @@ class FakeWorker(context: Context, workerParams: WorkerParameters) : CoroutineWo
private suspend fun executeFakeRequest(context: Context): Boolean {
return try {
val authCodeRepository = AuthCodeRepository(context)
- val accessTokenResponse = authCodeRepository.getAccessTokenSync(AuthenticationCodeRequestModel(FAKE_AUTH_CODE, 1))
+ val accessTokenResponse = authCodeRepository.getAccessTokenSyncV1(AuthenticationCodeRequestModel(FAKE_AUTH_CODE, 1))
val accessToken = accessTokenResponse.accessToken
DP3TKotlin.sendFakeInfectedRequest(context, ExposeeAuthMethodAuthorization(getAuthorizationHeader(accessToken)))
true
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
new file mode 100644
index 000000000..331276821
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
@@ -0,0 +1,6 @@
+package ch.admin.bag.dp3t.networking.models
+
+data class AuthenticationCodeResponseModelV2(
+ val checkInAccessToken: AuthenticationCodeResponseModel,
+ val dp3TAccessToken: AuthenticationCodeResponseModel
+)
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
index 6739426d4..090ab0fb0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
@@ -48,6 +48,7 @@ public class SecureStorage {
private static final String KEY_INFORM_TIME_REQ = "inform_time_req";
private static final String KEY_INFORM_CODE_REQ = "inform_code_req";
private static final String KEY_INFORM_TOKEN_REQ = "inform_token_req";
+ private static final String KEY_INFORM_TOKEN_CHECKIN_REQ = "inform_token_chekin_req";
private static final String KEY_ONBOARDING_COMPLETED = "onboarding_completed";
private static final String KEY_UPDATE_BOARDING_VERSION = "update_boarding_version";
private static final String KEY_LAST_SHOWN_CONTACT_ID = "last_shown_contact_id";
@@ -117,10 +118,11 @@ public void setInfectedDate(long date) {
prefs.edit().putLong(KEY_INFECTED_DATE, date).apply();
}
- public void saveInformTimeAndCodeAndToken(String informCode, String informToken) {
+ public void saveInformTimeAndCodeAndToken(String informCode, String dp3tInformToken, String checkinInformToken) {
prefs.edit().putLong(KEY_INFORM_TIME_REQ, System.currentTimeMillis())
.putString(KEY_INFORM_CODE_REQ, informCode)
- .putString(KEY_INFORM_TOKEN_REQ, informToken)
+ .putString(KEY_INFORM_TOKEN_REQ, dp3tInformToken)
+ .putString(KEY_INFORM_TOKEN_CHECKIN_REQ, checkinInformToken)
.apply();
}
@@ -128,6 +130,7 @@ public void clearInformTimeAndCodeAndToken() {
prefs.edit().remove(KEY_INFORM_TIME_REQ)
.remove(KEY_INFORM_CODE_REQ)
.remove(KEY_INFORM_TOKEN_REQ)
+ .remove(KEY_INFORM_TOKEN_CHECKIN_REQ)
.apply();
}
@@ -139,10 +142,14 @@ public String getLastInformCode() {
return prefs.getString(KEY_INFORM_CODE_REQ, null);
}
- public String getLastInformToken() {
+ public String getLastDP3TInformToken() {
return prefs.getString(KEY_INFORM_TOKEN_REQ, null);
}
+ public String getLastCheckinInformToken() {
+ return prefs.getString(KEY_INFORM_TOKEN_CHECKIN_REQ, null);
+ }
+
public boolean getOnboardingCompleted() {
return prefs.getBoolean(KEY_ONBOARDING_COMPLETED, false);
}
diff --git a/app/src/main/res/layout/fragment_inform.xml b/app/src/main/res/layout/fragment_inform.xml
index f1bfdc45e..31dafed18 100644
--- a/app/src/main/res/layout/fragment_inform.xml
+++ b/app/src/main/res/layout/fragment_inform.xml
@@ -83,13 +83,13 @@
android:text="@string/inform_code_text" />
Date: Thu, 6 May 2021 15:54:39 +0200
Subject: [PATCH 067/207] Implement ReallyNotShareFragment
---
.../checkin/generateqrcode/QRCodeViewModel.kt | 5 +-
.../admin/bag/dp3t/inform/InformFragment.kt | 2 +-
.../admin/bag/dp3t/inform/InformViewModel.kt | 67 +++++++++----------
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 29 ++++++++
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 11 +--
.../fragment_inform_really_not_share.xml | 53 +++++++++++++++
6 files changed, 126 insertions(+), 41 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
create mode 100644 app/src/main/res/layout/fragment_inform_really_not_share.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index 1a8c94918..cef97bebe 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.crowdnotifier.android.sdk.CrowdNotifier
import org.crowdnotifier.android.sdk.model.VenueInfo
+import org.crowdnotifier.android.sdk.utils.Base64Util
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
@@ -27,7 +28,7 @@ import kotlin.math.max
private const val MASTER_PUBLIC_KEY_BASE64 =
- "TqRYigTM6YVO7/UJQuu3199mRqj0cSTp4DXCFlxbz9UqDLrASr07C9HJVWYtl08V7xGEGSSXWbQSRfRt/bqqDK0HQQGnZ/VWZxToo88txtgQ1ij7pYJwaBHAGGndLICL"
+ "lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"
private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
private const val ONE_HOUR_IN_MILLIS = 60 * ONE_MINUTE_IN_MILLIS
private const val AUTOMATIC_CHECKOUT_DELAY_MS = 12 * ONE_HOUR_IN_MILLIS
@@ -89,7 +90,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
swissCovidLocationData.toByteArray(),
System.currentTimeMillis() / 1000,
(System.currentTimeMillis() + QR_CODE_VALIDITY_DURATION_MS) / 1000,
- Base64.decode(MASTER_PUBLIC_KEY_BASE64, Base64.DEFAULT)
+ Base64Util.fromBase64(MASTER_PUBLIC_KEY_BASE64)
)
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 3e1fbc3dc..6274005a5 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
@@ -155,7 +155,7 @@ class InformFragment : Fragment() {
private fun handleInformExposedRequestError(throwable: Throwable) {
when (throwable) {
is ResponseError -> showErrorDialog(InformRequestError.RED_STATUS_ERROR, throwable.statusCode.toString())
- is CancellationException -> showErrorDialog(InformRequestError.RED_USER_CANCELLED_SHARE)
+ is CancellationException -> showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
is ApiException -> showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR, throwable.statusCode.toString())
else -> showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 61a50d759..5e7a1a7a8 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -3,6 +3,7 @@ package ch.admin.bag.dp3t.inform
import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
@@ -20,6 +21,7 @@ import org.dpppt.android.sdk.DP3T
import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.models.DayDate
import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization
+import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -37,7 +39,7 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
private val secureStorage = SecureStorage.getInstance(application)
//TODO: Persist these Items for DKA cases
- val selectableDiaryItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
+ var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
fun userUpload() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
@@ -62,43 +64,19 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
fun authenticateInputAndGetDP3TAccessToken(authCode: String) = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
- val lastTimestamp = secureStorage.lastInformRequestTime
- val lastDP3TAuthToken = secureStorage.lastDP3TInformToken
- if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && lastDP3TAuthToken != null) {
- emit(Resource.success(lastDP3TAuthToken))
- } else {
- try {
- val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
- secureStorage.saveInformTimeAndCodeAndToken(
- authCode,
- accessTokens.dp3TAccessToken.accessToken,
- accessTokens.checkInAccessToken.accessToken
- )
- emit(Resource.success(data = accessTokens.dp3TAccessToken.accessToken))
- } catch (exception: Exception) {
- emit(Resource.error(data = null, exception = exception))
- }
+ try {
+ emit(Resource.success(loadAccessToken(authCode, isCheckin = false)))
+ } catch (exception: Exception) {
+ emit(Resource.error(data = null, exception = exception))
}
}
fun authenticateInputAndGetCheckinAccessToken(authCode: String) = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
- val lastTimestamp = secureStorage.lastInformRequestTime
- val lastCheckinAuthToken = secureStorage.lastCheckinInformToken
- if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && lastCheckinAuthToken != null) {
- emit(Resource.success(lastCheckinAuthToken))
- } else {
- try {
- val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
- secureStorage.saveInformTimeAndCodeAndToken(
- authCode,
- accessTokens.dp3TAccessToken.accessToken,
- accessTokens.checkInAccessToken.accessToken
- )
- emit(Resource.success(data = accessTokens.checkInAccessToken.accessToken))
- } catch (exception: Exception) {
- emit(Resource.error(data = null, exception = exception))
- }
+ try {
+ emit(Resource.success(loadAccessToken(authCode, isCheckin = true)))
+ } catch (exception: Exception) {
+ emit(Resource.error(data = null, exception = exception))
}
}
@@ -139,9 +117,30 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
result?.let { emit(it) }
}
+ private suspend fun loadAccessToken(authCode: String, isCheckin: Boolean): String {
+ val lastTimestamp = secureStorage.lastInformRequestTime
+ val authToken = if (isCheckin) secureStorage.lastCheckinInformToken else secureStorage.lastDP3TInformToken
+ if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && authToken != null) {
+ return authToken
+ } else {
+ val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
+ secureStorage.saveInformTimeAndCodeAndToken(
+ authCode, accessTokens.dp3TAccessToken.accessToken, accessTokens.checkInAccessToken.accessToken
+ )
+ return if (isCheckin) accessTokens.checkInAccessToken.accessToken else accessTokens.dp3TAccessToken.accessToken
+ }
+ }
+
+ fun filterSelectableDiaryItems() {
+ selectableCheckinItems = selectableCheckinItems.filter {
+ Date(it.diaryEntry.departureTime).after(JwtUtil.getOnsetDate(secureStorage.lastCheckinInformToken))
+ }
+ }
+
+
private fun getUserUploadPayload(): UserUploadPayload {
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
- selectableDiaryItems.filter {
+ selectableCheckinItems.filter {
it.isSelected
}.map {
CrowdNotifier.generateUserUploadInfo(it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
new file mode 100644
index 000000000..14ae6e3e3
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -0,0 +1,29 @@
+package ch.admin.bag.dp3t.inform
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.databinding.FragmentInformReallyNotShareBinding
+import ch.admin.bag.dp3t.util.showFragment
+
+class ReallyNotShareFragment : Fragment() {
+
+ companion object {
+ fun newInstance() = ReallyNotShareFragment()
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return FragmentInformReallyNotShareBinding.inflate(inflater).apply {
+ tryAgainButton.setOnClickListener {
+ //TODO: Implement sharing
+ }
+ dontSendButton.setOnClickListener {
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ }
+ }.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index ffa9a0d43..07658f960 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -26,22 +26,24 @@ class ShareCheckinsFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentShareCheckinsBinding.inflate(inflater).apply {
+ informViewModel.filterSelectableDiaryItems()
val adapter = CheckinAdapter()
checkinsRecyclerView.adapter = adapter
checkinsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
- adapter.setData(informViewModel.selectableDiaryItems)
+ adapter.setData(informViewModel.selectableCheckinItems)
adapter.itemSelectionListener { selectedItem, selected ->
- informViewModel.selectableDiaryItems.find { it.diaryEntry == selectedItem }?.isSelected = selected
+ informViewModel.selectableCheckinItems.find { it.diaryEntry == selectedItem }?.isSelected = selected
}
selectAllCheckbox.setOnCheckedChangeListener { _, isChecked ->
- informViewModel.selectableDiaryItems.forEach { it.isSelected = isChecked }
- adapter.setData(informViewModel.selectableDiaryItems)
+ informViewModel.selectableCheckinItems.forEach { it.isSelected = isChecked }
+ adapter.setData(informViewModel.selectableCheckinItems)
}
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
}
sendButton.setOnClickListener {
+ //TODO Use auth Token for user upload
informViewModel.userUpload().observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
@@ -60,4 +62,5 @@ class ShareCheckinsFragment : Fragment() {
return binding.root
}
+
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_inform_really_not_share.xml b/app/src/main/res/layout/fragment_inform_really_not_share.xml
new file mode 100644
index 000000000..bdb027fb2
--- /dev/null
+++ b/app/src/main/res/layout/fragment_inform_really_not_share.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From d3cfc52ca25610b609e243c5220d5784a7b1368d Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 10 May 2021 14:40:29 +0200
Subject: [PATCH 068/207] Update SDK and make some changes on Inform Flow
---
app/build.gradle | 2 +-
.../networking/TraceKeysRepository.java | 2 +-
.../admin/bag/dp3t/inform/InformFragment.kt | 23 ++--
.../admin/bag/dp3t/inform/InformViewModel.kt | 3 +-
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 2 +-
.../bag/dp3t/networking/AuthCodeRepository.kt | 18 ++-
.../bag/dp3t/networking/AuthCodeService.kt | 2 +-
app/src/main/proto/Crowdnotifier.proto | 2 +-
app/src/main/res/layout/fragment_inform.xml | 128 ++++++++++--------
9 files changed, 101 insertions(+), 81 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7e96f1cd6..b31c06d3a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -207,7 +207,7 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-8'
+ implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-9'
implementation 'androidx.appcompat:appcompat:1.2.0'
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index f502b3d02..61aaad22f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -88,7 +88,7 @@ private List handleSuccessfulResponse(Response {
- setProgressDialogVisible(true)
+ setLoadingViewVisible(true)
binding.sendButton.isEnabled = false
}
Status.ERROR -> {
- setProgressDialogVisible(false)
+ setLoadingViewVisible(false)
it.exception?.let { exception ->
handleAuthenticateRequestError(exception)
}
@@ -133,18 +131,18 @@ class InformFragment : Fragment() {
informViewModel.informExposed(accessToken, requireActivity()).observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
- setProgressDialogVisible(true)
+ setLoadingViewVisible(true)
binding.sendButton.isEnabled = false
}
Status.ERROR -> {
- setProgressDialogVisible(false)
+ setLoadingViewVisible(false)
it.exception?.let { exception ->
handleInformExposedRequestError(exception)
}
binding.sendButton.isEnabled = true
}
Status.SUCCESS -> {
- setProgressDialogVisible(false)
+ setLoadingViewVisible(false)
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
binding.sendButton.isEnabled = true
}
@@ -162,13 +160,8 @@ class InformFragment : Fragment() {
throwable.printStackTrace()
}
- private fun setProgressDialogVisible(isVisible: Boolean) {
- if (isVisible && !progressDialog.isShowing) {
- progressDialog.show()
- }
- if (!isVisible && progressDialog.isShowing) {
- progressDialog.dismiss()
- }
+ private fun setLoadingViewVisible(isVisible: Boolean) {
+ binding.loadingView.isVisible = isVisible
}
private fun askUserToEnableTracing(authCode: String) {
@@ -199,7 +192,7 @@ class InformFragment : Fragment() {
private fun setInvalidCodeErrorVisible(visible: Boolean) {
binding.apply {
informInvalidCodeError.isVisible = visible
- informInputText.isVisible = visible
+ informInputText.isVisible = !visible
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 5e7a1a7a8..47fbf47e7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -3,7 +3,6 @@ package ch.admin.bag.dp3t.inform
import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
@@ -66,7 +65,7 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
emit(Resource.loading(data = null))
try {
emit(Resource.success(loadAccessToken(authCode, isCheckin = false)))
- } catch (exception: Exception) {
+ } catch (exception: Throwable) {
emit(Resource.error(data = null, exception = exception))
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index 07658f960..a3e6480bf 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -40,7 +40,7 @@ class ShareCheckinsFragment : Fragment() {
}
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
-
+ //TODO: Implement
}
sendButton.setOnClickListener {
//TODO Use auth Token for user upload
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
index 8976773ce..ce4156d39 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
@@ -10,8 +10,6 @@
package ch.admin.bag.dp3t.networking
import android.content.Context
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
import ch.admin.bag.dp3t.BuildConfig
import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
@@ -23,9 +21,7 @@ import okhttp3.Cache
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import org.dpppt.android.sdk.DP3T
-import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.backend.UserAgentInterceptor
-import org.dpppt.android.sdk.internal.logger.Logger
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
@@ -71,5 +67,17 @@ class AuthCodeRepository(context: Context) {
return@withContext response.body() ?: throw ResponseError(response.raw())
}
- suspend fun getAccessToken(authCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2 = authCodeService.getAccessToken(authCode)
+ suspend fun getAccessToken(authCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2 =
+ withContext(Dispatchers.IO) {
+ val response = authCodeService.getAccessTokenV2(authCode)
+ if (!response.isSuccessful) {
+ if (response.code() == 404) {
+ throw InvalidCodeError()
+ } else {
+ throw ResponseError(response.raw())
+ }
+ }
+ return@withContext response.body() ?: throw ResponseError(response.raw())
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
index 5b206848c..24308fea0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
@@ -24,6 +24,6 @@ interface AuthCodeService {
@Headers("accept: */*", "content-type: application/json")
@POST("v2/onset")
- suspend fun getAccessToken(@Body code: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2
+ suspend fun getAccessTokenV2(@Body code: AuthenticationCodeRequestModel): Response
}
\ No newline at end of file
diff --git a/app/src/main/proto/Crowdnotifier.proto b/app/src/main/proto/Crowdnotifier.proto
index 393460e66..bc37f7852 100644
--- a/app/src/main/proto/Crowdnotifier.proto
+++ b/app/src/main/proto/Crowdnotifier.proto
@@ -12,7 +12,7 @@ message ProblematicEvent {
int32 version = 1;
bytes identity = 2;
bytes secretKeyForIdentity = 3;
- int64 day = 4;
+ int64 day = 4; // in seconds
bytes encryptedAssociatedData = 5;
bytes cipherTextNonce = 6;
}
diff --git a/app/src/main/res/layout/fragment_inform.xml b/app/src/main/res/layout/fragment_inform.xml
index 31dafed18..29ef7ee8e 100644
--- a/app/src/main/res/layout/fragment_inform.xml
+++ b/app/src/main/res/layout/fragment_inform.xml
@@ -11,8 +11,8 @@
+ android:layout_height="match_parent"
+ android:background="@color/white">
-
-
-
+ android:layout_height="wrap_content">
+ android:layout_marginTop="@dimen/spacing_huge"
+ android:orientation="vertical">
+
+
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="@dimen/spacing_very_small"
+ android:background="?selectableItemBackground"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:padding="@dimen/spacing_medium"
+ android:visibility="gone"
+ tools:visibility="visible">
+
+
+
+
+
+
+ android:paddingStart="@dimen/spacing_huge"
+ android:paddingEnd="@dimen/spacing_huge"
+ android:text="@string/inform_code_text" />
+
+
+
+
-
-
-
+ android:layout_height="match_parent"
+ android:background="@color/transparent_milk"
+ android:visibility="gone">
-
+
+
+
-
+
\ No newline at end of file
From 97b0113268f969213867658987d9341198133a72 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 10 May 2021 15:28:56 +0200
Subject: [PATCH 069/207] Implement ReallyNotShareFragment
---
.../admin/bag/dp3t/inform/InformFragment.kt | 127 +---------------
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 30 +++-
.../dp3t/inform/TraceKeyShareBaseFragment.kt | 140 ++++++++++++++++++
.../fragment_inform_really_not_share.xml | 102 ++++++++-----
4 files changed, 235 insertions(+), 164 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
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 1da02e6cf..491ab0cbc 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
@@ -13,33 +13,17 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
-import androidx.appcompat.app.AlertDialog
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.databinding.FragmentInformBinding
-import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.inform.views.ChainedEditText
import ch.admin.bag.dp3t.inform.views.ChainedEditText.ChainedEditTextListener
-import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
-import ch.admin.bag.dp3t.networking.errors.ResponseError
-import ch.admin.bag.dp3t.util.ENExceptionHelper
import ch.admin.bag.dp3t.util.PhoneUtil
import ch.admin.bag.dp3t.util.showFragment
-import ch.admin.bag.dp3t.viewmodel.TracingViewModel
-import com.google.android.gms.common.api.ApiException
-import org.dpppt.android.sdk.DP3T
-import org.dpppt.android.sdk.internal.logger.Logger
-import java.util.concurrent.CancellationException
private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}"
-class InformFragment : Fragment() {
-
- private val tracingViewModel: TracingViewModel by activityViewModels()
- private val informViewModel: InformViewModel by activityViewModels()
+class InformFragment : TraceKeyShareBaseFragment() {
private lateinit var binding: FragmentInformBinding
@@ -77,12 +61,7 @@ class InformFragment : Fragment() {
sendButton.isEnabled = false
setInvalidCodeErrorVisible(false)
val authCode = covidcodeInput.text
- val isTracingEnabled = DP3T.isTracingEnabled(requireContext())
- if (isTracingEnabled) {
- authenticateInputAndInformExposed(authCode)
- } else {
- askUserToEnableTracing(authCode)
- }
+ authenticateInputAndInformExposed(authCode)
}
cancelButton.setOnClickListener { requireActivity().finish() }
informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
@@ -97,114 +76,24 @@ class InformFragment : Fragment() {
binding.covidcodeInput.requestFocus()
}
- private fun authenticateInputAndInformExposed(authCode: String) {
- informViewModel.authenticateInputAndGetDP3TAccessToken(authCode).observe(viewLifecycleOwner) {
- when (it.status) {
- Status.LOADING -> {
- setLoadingViewVisible(true)
- binding.sendButton.isEnabled = false
- }
- Status.ERROR -> {
- setLoadingViewVisible(false)
- it.exception?.let { exception ->
- handleAuthenticateRequestError(exception)
- }
- binding.sendButton.isEnabled = true
- }
- Status.SUCCESS -> {
- it.data?.let { accessToken -> informExposed(accessToken) }
- }
- }
- }
- }
-
- private fun handleAuthenticateRequestError(throwable: Throwable) {
- when (throwable) {
- is InvalidCodeError -> setInvalidCodeErrorVisible(true)
- is ResponseError -> showErrorDialog(InformRequestError.BLACK_STATUS_ERROR, throwable.statusCode.toString())
- else -> showErrorDialog(InformRequestError.BLACK_MISC_NETWORK_ERROR)
- }
- throwable.printStackTrace()
- }
-
- private fun informExposed(accessToken: String) {
- informViewModel.informExposed(accessToken, requireActivity()).observe(viewLifecycleOwner) {
- when (it.status) {
- Status.LOADING -> {
- setLoadingViewVisible(true)
- binding.sendButton.isEnabled = false
- }
- Status.ERROR -> {
- setLoadingViewVisible(false)
- it.exception?.let { exception ->
- handleInformExposedRequestError(exception)
- }
- binding.sendButton.isEnabled = true
- }
- Status.SUCCESS -> {
- setLoadingViewVisible(false)
- showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
- binding.sendButton.isEnabled = true
- }
- }
- }
- }
- private fun handleInformExposedRequestError(throwable: Throwable) {
- when (throwable) {
- is ResponseError -> showErrorDialog(InformRequestError.RED_STATUS_ERROR, throwable.statusCode.toString())
- is CancellationException -> showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
- is ApiException -> showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR, throwable.statusCode.toString())
- else -> showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR)
- }
- throwable.printStackTrace()
- }
-
- private fun setLoadingViewVisible(isVisible: Boolean) {
+ override fun setLoadingViewVisible(isVisible: Boolean) {
binding.loadingView.isVisible = isVisible
}
- private fun askUserToEnableTracing(authCode: String) {
- AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(R.string.android_inform_tracing_enabled_explanation)
- .setOnCancelListener { binding.sendButton.isEnabled = true }
- .setNegativeButton(R.string.cancel) { _, _ -> binding.sendButton.isEnabled = true }
- .setPositiveButton(R.string.activate_tracing_button) { _, _ -> enableTracing(authCode) }
- .show()
- }
-
- private fun enableTracing(authCode: String) {
- tracingViewModel.enableTracing(requireActivity(),
- { authenticateInputAndInformExposed(authCode) },
- { e: Exception? ->
- val message = ENExceptionHelper.getErrorMessage(e, activity)
- Logger.e(TAG, message)
- AlertDialog.Builder(requireActivity(), R.style.NextStep_AlertDialogStyle)
- .setTitle(R.string.android_en_start_failure)
- .setMessage(message)
- .setOnDismissListener { binding.sendButton.isEnabled = true }
- .setPositiveButton(R.string.android_button_ok) { _, _ -> }
- .show()
- }
- ) { binding.sendButton.isEnabled = true }
+ override fun setSendButtonEnabled(isEnabled: Boolean) {
+ binding.sendButton.isEnabled = isEnabled
}
- private fun setInvalidCodeErrorVisible(visible: Boolean) {
+ override fun setInvalidCodeErrorVisible(visible: Boolean) {
binding.apply {
informInvalidCodeError.isVisible = visible
informInputText.isVisible = !visible
}
}
- private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
- val errorDialogBuilder = AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(error.errorMessage)
- .setPositiveButton(R.string.android_button_ok) { _, _ -> }
- val errorCode = error.getErrorCode(addErrorCode)
- val errorCodeView = layoutInflater.inflate(R.layout.view_dialog_error_code, view as ViewGroup?, false) as TextView
- errorCodeView.text = errorCode
- errorDialogBuilder.setView(errorCodeView)
- errorDialogBuilder.show()
+ override fun performNotShareAction() {
+ showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index 14ae6e3e3..f633496ae 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -4,26 +4,46 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
+import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentInformReallyNotShareBinding
import ch.admin.bag.dp3t.util.showFragment
-class ReallyNotShareFragment : Fragment() {
+class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
companion object {
fun newInstance() = ReallyNotShareFragment()
}
+ private lateinit var binding: FragmentInformReallyNotShareBinding
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return FragmentInformReallyNotShareBinding.inflate(inflater).apply {
+ binding = FragmentInformReallyNotShareBinding.inflate(inflater).apply {
+ (requireActivity() as InformActivity).allowBackButton(false)
tryAgainButton.setOnClickListener {
- //TODO: Implement sharing
+ informViewModel.getLastAuthCode()?.let { authenticateInputAndInformExposed(it) }
}
dontSendButton.setOnClickListener {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
}
- }.root
+ }
+ return binding.root
+ }
+
+ override fun setLoadingViewVisible(isVisible: Boolean) {
+ binding.loadingView.isVisible = isVisible
+ }
+
+ override fun setSendButtonEnabled(isEnabled: Boolean) {
+ binding.tryAgainButton.isEnabled = isEnabled
+ }
+
+ override fun setInvalidCodeErrorVisible(isVisible: Boolean) {
+ // The Code must be valid in this fragment otherwise we cannot get here
+ }
+
+ override fun performNotShareAction() {
+ // stay on this screen
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
new file mode 100644
index 000000000..c6153c94e
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
@@ -0,0 +1,140 @@
+package ch.admin.bag.dp3t.inform
+
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.inform.models.Status
+import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
+import ch.admin.bag.dp3t.networking.errors.ResponseError
+import ch.admin.bag.dp3t.util.ENExceptionHelper
+import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.viewmodel.TracingViewModel
+import com.google.android.gms.common.api.ApiException
+import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.internal.logger.Logger
+import java.util.concurrent.CancellationException
+
+abstract class TraceKeyShareBaseFragment : Fragment() {
+
+
+ companion object {
+ private const val TAG = "TraceKeyShareBaseFragment"
+ }
+
+ protected val informViewModel: InformViewModel by activityViewModels()
+ private val tracingViewModel: TracingViewModel by activityViewModels()
+
+ abstract fun setLoadingViewVisible(isVisible: Boolean)
+ abstract fun setSendButtonEnabled(isEnabled: Boolean)
+ abstract fun setInvalidCodeErrorVisible(isVisible: Boolean)
+ abstract fun performNotShareAction()
+
+ protected fun authenticateInputAndInformExposed(authCode: String) {
+ val isTracingEnabled = DP3T.isTracingEnabled(requireContext())
+ if (!isTracingEnabled) {
+ askUserToEnableTracing(authCode)
+ return
+ }
+
+ informViewModel.authenticateInputAndGetDP3TAccessToken(authCode).observe(viewLifecycleOwner) {
+ when (it.status) {
+ Status.LOADING -> {
+ setLoadingViewVisible(true)
+ setSendButtonEnabled(false)
+ }
+ Status.ERROR -> {
+ setLoadingViewVisible(false)
+ it.exception?.let { exception ->
+ handleAuthenticateRequestError(exception)
+ }
+ setSendButtonEnabled(true)
+ }
+ Status.SUCCESS -> {
+ it.data?.let { accessToken -> informExposed(accessToken) }
+ }
+ }
+ }
+ }
+
+ private fun handleAuthenticateRequestError(throwable: Throwable) {
+ when (throwable) {
+ is InvalidCodeError -> setInvalidCodeErrorVisible(true)
+ is ResponseError -> showErrorDialog(InformRequestError.BLACK_STATUS_ERROR, throwable.statusCode.toString())
+ else -> showErrorDialog(InformRequestError.BLACK_MISC_NETWORK_ERROR)
+ }
+ throwable.printStackTrace()
+ }
+
+ private fun informExposed(accessToken: String) {
+ informViewModel.informExposed(accessToken, requireActivity()).observe(viewLifecycleOwner) {
+ when (it.status) {
+ Status.LOADING -> {
+ setLoadingViewVisible(true)
+ setSendButtonEnabled(false)
+ }
+ Status.ERROR -> {
+ setLoadingViewVisible(false)
+ it.exception?.let { exception ->
+ handleInformExposedRequestError(exception)
+ }
+ setSendButtonEnabled(true)
+ }
+ Status.SUCCESS -> {
+ setLoadingViewVisible(false)
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ setSendButtonEnabled(true)
+ }
+ }
+ }
+ }
+
+ private fun handleInformExposedRequestError(throwable: Throwable) {
+ when (throwable) {
+ is ResponseError -> showErrorDialog(InformRequestError.RED_STATUS_ERROR, throwable.statusCode.toString())
+ is CancellationException -> performNotShareAction()
+ is ApiException -> showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR, throwable.statusCode.toString())
+ else -> showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR)
+ }
+ throwable.printStackTrace()
+ }
+
+ private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
+ val errorDialogBuilder = AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(error.errorMessage)
+ .setPositiveButton(R.string.android_button_ok) { _, _ -> }
+ val errorCode = error.getErrorCode(addErrorCode)
+ val errorCodeView = layoutInflater.inflate(R.layout.view_dialog_error_code, view as ViewGroup?, false) as TextView
+ errorCodeView.text = errorCode
+ errorDialogBuilder.setView(errorCodeView)
+ errorDialogBuilder.show()
+ }
+
+ private fun askUserToEnableTracing(authCode: String) {
+ AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.android_inform_tracing_enabled_explanation)
+ .setOnCancelListener { setSendButtonEnabled(true) }
+ .setNegativeButton(R.string.cancel) { _, _ -> setSendButtonEnabled(true) }
+ .setPositiveButton(R.string.activate_tracing_button) { _, _ -> enableTracing(authCode) }
+ .show()
+ }
+
+ private fun enableTracing(authCode: String) {
+ tracingViewModel.enableTracing(requireActivity(),
+ { authenticateInputAndInformExposed(authCode) },
+ { e: Exception? ->
+ val message = ENExceptionHelper.getErrorMessage(e, activity)
+ Logger.e(TAG, message)
+ AlertDialog.Builder(requireActivity(), R.style.NextStep_AlertDialogStyle)
+ .setTitle(R.string.android_en_start_failure)
+ .setMessage(message)
+ .setOnDismissListener { setSendButtonEnabled(true) }
+ .setPositiveButton(R.string.android_button_ok) { _, _ -> }
+ .show()
+ }
+ ) { setSendButtonEnabled(true) }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_inform_really_not_share.xml b/app/src/main/res/layout/fragment_inform_really_not_share.xml
index bdb027fb2..8ae2804a1 100644
--- a/app/src/main/res/layout/fragment_inform_really_not_share.xml
+++ b/app/src/main/res/layout/fragment_inform_really_not_share.xml
@@ -1,53 +1,75 @@
-
+ android:layout_height="wrap_content">
-
+ android:orientation="vertical">
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+ android:layout_height="match_parent"
+ android:background="@color/transparent_milk"
+ android:visibility="gone">
+
+
+
+
+
+
+
From edff132a894d82544760c81989845003d727fcff Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 11 May 2021 13:38:54 +0200
Subject: [PATCH 070/207] Add Loading Animation and Error Handling to
ShareCheckinsFragment
---
.../networking/UserUploadRepository.kt | 3 +-
.../checkin/networking/UserUploadService.kt | 5 +-
.../admin/bag/dp3t/inform/InformFragment.kt | 2 +-
.../bag/dp3t/inform/InformRequestError.java | 5 +-
.../admin/bag/dp3t/inform/InformViewModel.kt | 59 ++++----
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 9 +-
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 62 ++++++--
.../dp3t/inform/TraceKeyShareBaseFragment.kt | 20 ++-
.../res/layout/fragment_share_checkins.xml | 142 ++++++++++--------
9 files changed, 184 insertions(+), 123 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
index 9e405ea20..373947cdc 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
@@ -25,7 +25,8 @@ class UserUploadRepository {
userUploadService = retrofit.create(UserUploadService::class.java)
}
- suspend fun userUpload(userUploadPayload: UserUploadPayload) = userUploadService.userUpload(userUploadPayload)
+ suspend fun userUpload(userUploadPayload: UserUploadPayload, authorizationHeader: String) =
+ userUploadService.userUpload(userUploadPayload, authorizationHeader)
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
index 272722ab7..810c4cf08 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadService.kt
@@ -2,11 +2,14 @@ package ch.admin.bag.dp3t.checkin.networking
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import retrofit2.http.Body
+import retrofit2.http.Header
import retrofit2.http.POST
interface UserUploadService {
@POST("v3/userupload")
- suspend fun userUpload(@Body userUploadPayload: UserUploadPayload)
+ suspend fun userUpload(
+ @Body userUploadPayload: UserUploadPayload, @Header("Authorization") authorizationHeader: String?
+ )
}
\ No newline at end of file
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 491ab0cbc..9fee26a5d 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
@@ -47,7 +47,7 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
})
- informViewModel.getLastAuthCode()?.let {
+ informViewModel.getLastCovidcode()?.let {
covidcodeInput.text = it
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformRequestError.java b/app/src/main/java/ch/admin/bag/dp3t/inform/InformRequestError.java
index eaf41f6aa..2e79b2e21 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformRequestError.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformRequestError.java
@@ -7,7 +7,6 @@
*
* SPDX-License-Identifier: MPL-2.0
*/
-
package ch.admin.bag.dp3t.inform;
import ch.admin.bag.dp3t.R;
@@ -19,7 +18,9 @@ public enum InformRequestError {
RED_STATUS_ERROR(R.string.unexpected_error_with_retry, "ARST"),
RED_USER_CANCELLED_SHARE(R.string.user_cancelled_key_sharing_error, "ARUSCCD"),
RED_EXPOSURE_API_ERROR(R.string.unexpected_error_title, "AREA"),
- RED_MISC_NETWORK_ERROR(R.string.network_error, "ARNETWE");
+ RED_MISC_NETWORK_ERROR(R.string.network_error, "ARNETWE"),
+ USER_UPLOAD_NETWORK_ERROR(R.string.network_error, "AUANETWE"),
+ USER_UPLOAD_UNKONWN_ERROR(R.string.network_error, "AUAUKWE");
private final int errorMessage;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 47fbf47e7..00b114aca 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -3,6 +3,7 @@ package ch.admin.bag.dp3t.inform
import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.liveData
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
@@ -29,60 +30,53 @@ private const val USER_UPLOAD_VERSION = 3
private const val TIMEOUT_VALID_CODE = 1000L * 60 * 5
private const val MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L
private const val ISOLATION_DURATION_DAYS = 14L
+private const val KEY_HAS_SHARED_DP3T_KEYS = "KEY_HAS_SHARED_DP3T_KEYS"
-class InformViewModel(application: Application) : AndroidViewModel(application) {
+class InformViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) {
private val authCodeRepository = AuthCodeRepository(application)
private val userUploadRepository = UserUploadRepository()
private val diaryStorage = DiaryStorage.getInstance(application)
private val secureStorage = SecureStorage.getInstance(application)
- //TODO: Persist these Items for DKA cases
var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
+ var hasSharedDP3TKeys: Boolean
+ get() = state.get(KEY_HAS_SHARED_DP3T_KEYS) ?: false
+ set(value) = state.set(KEY_HAS_SHARED_DP3T_KEYS, value)
fun userUpload() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
- emit(Resource.success(data = userUploadRepository.userUpload(getUserUploadPayload())))
+ val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
+ emit(Resource.success(data = userUploadRepository.userUpload(getUserUploadPayload(), authorizationHeader)))
} catch (exception: Exception) {
emit(Resource.error(data = null, exception = exception))
}
}
- fun getLastAuthCode(): String? {
- val lastCode = secureStorage.lastInformCode
- val lastToken = secureStorage.lastDP3TInformToken
+ fun getLastCovidcode(): String? {
+ val lastCovidcode = secureStorage.lastInformCode
- if (System.currentTimeMillis() - secureStorage.lastInformRequestTime < TIMEOUT_VALID_CODE) {
- return lastCode
- } else if (lastCode != null || lastToken != null) {
- secureStorage.clearInformTimeAndCodeAndToken()
+ return if (System.currentTimeMillis() - secureStorage.lastInformRequestTime < TIMEOUT_VALID_CODE) {
+ lastCovidcode
+ } else {
+ null
}
- return null
}
- fun authenticateInputAndGetDP3TAccessToken(authCode: String) = liveData(Dispatchers.IO) {
+ fun authenticateCovidcode(covidcode: String) = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
- emit(Resource.success(loadAccessToken(authCode, isCheckin = false)))
+ emit(Resource.success(loadAccessTokens(covidcode)))
} catch (exception: Throwable) {
emit(Resource.error(data = null, exception = exception))
}
}
- fun authenticateInputAndGetCheckinAccessToken(authCode: String) = liveData(Dispatchers.IO) {
+ fun informExposed(activity: Activity) = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
- try {
- emit(Resource.success(loadAccessToken(authCode, isCheckin = true)))
- } catch (exception: Exception) {
- emit(Resource.error(data = null, exception = exception))
- }
- }
-
- fun informExposed(accessToken: String, activity: Activity) = liveData(Dispatchers.IO) {
- emit(Resource.loading(data = null))
- val authorizationHeader = getAuthorizationHeader(accessToken)
- val onsetDate = JwtUtil.getOnsetDate(accessToken)
+ val authorizationHeader = getAuthorizationHeader(secureStorage.lastDP3TInformToken)
+ val onsetDate = JwtUtil.getOnsetDate(secureStorage.lastDP3TInformToken)
var result: Resource? = null
@@ -104,6 +98,7 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(ISOLATION_DURATION_DAYS)
secureStorage.isolationEndDialogTimestamp = isolationEndDialogTimestamp
result = Resource.success(Unit)
+ hasSharedDP3TKeys = true
continuation.resume(Unit)
}
@@ -116,17 +111,15 @@ class InformViewModel(application: Application) : AndroidViewModel(application)
result?.let { emit(it) }
}
- private suspend fun loadAccessToken(authCode: String, isCheckin: Boolean): String {
+ private suspend fun loadAccessTokens(covidcode: String) {
val lastTimestamp = secureStorage.lastInformRequestTime
- val authToken = if (isCheckin) secureStorage.lastCheckinInformToken else secureStorage.lastDP3TInformToken
- if (System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && authToken != null) {
- return authToken
- } else {
- val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(authCode, 0))
+ val accessToken = secureStorage.lastDP3TInformToken
+ val lastCovidcode = secureStorage.lastInformCode
+ if (!(System.currentTimeMillis() - lastTimestamp < TIMEOUT_VALID_CODE && accessToken != null) || covidcode != lastCovidcode) {
+ val accessTokens = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(covidcode, 0))
secureStorage.saveInformTimeAndCodeAndToken(
- authCode, accessTokens.dp3TAccessToken.accessToken, accessTokens.checkInAccessToken.accessToken
+ covidcode, accessTokens.dp3TAccessToken.accessToken, accessTokens.checkInAccessToken.accessToken
)
- return if (isCheckin) accessTokens.checkInAccessToken.accessToken else accessTokens.dp3TAccessToken.accessToken
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index f633496ae..2f82167d3 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.databinding.FragmentInformReallyNotShareBinding
import ch.admin.bag.dp3t.util.showFragment
@@ -21,10 +22,14 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
binding = FragmentInformReallyNotShareBinding.inflate(inflater).apply {
(requireActivity() as InformActivity).allowBackButton(false)
tryAgainButton.setOnClickListener {
- informViewModel.getLastAuthCode()?.let { authenticateInputAndInformExposed(it) }
+ informViewModel.getLastCovidcode()?.let { authenticateInputAndInformExposed(it) }
}
dontSendButton.setOnClickListener {
- showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ if (DiaryStorage.getInstance(requireContext()).entries.isNotEmpty()) {
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ } else {
+ //TODO: Show "Thanks for nothing" Fragment
+ }
}
}
return binding.root
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index a3e6480bf..7dbf3af42 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -5,6 +5,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
@@ -12,6 +15,7 @@ import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentShareCheckinsBinding
import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.util.showFragment
+import retrofit2.HttpException
class ShareCheckinsFragment : Fragment() {
@@ -40,27 +44,55 @@ class ShareCheckinsFragment : Fragment() {
}
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
- //TODO: Implement
+ if (informViewModel.hasSharedDP3TKeys) {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ } else {
+ //TODO: Show "Thanks for nothing" Fragment
+ }
}
- sendButton.setOnClickListener {
- //TODO Use auth Token for user upload
- informViewModel.userUpload().observe(viewLifecycleOwner) {
- when (it.status) {
- Status.LOADING -> {
- //TODO: Show loading animation
- }
- Status.ERROR -> {
- //TODO: Handle error
- }
- Status.SUCCESS -> {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- }
+ sendButton.setOnClickListener { performUserUpload() }
+ }
+ return binding.root
+ }
+
+ private fun performUserUpload() {
+
+ informViewModel.userUpload().observe(viewLifecycleOwner) {
+ binding.loadingView.isVisible = it.status == Status.LOADING
+ when (it.status) {
+ Status.LOADING -> showLoadingView(true)
+ Status.ERROR -> {
+ showLoadingView(false)
+ if (it.exception is HttpException) {
+ showErrorDialog(InformRequestError.USER_UPLOAD_NETWORK_ERROR, it.exception.code().toString())
+ } else {
+ showErrorDialog(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, it.exception?.message)
}
}
+ Status.SUCCESS -> {
+ showLoadingView(false)
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ }
}
}
- return binding.root
}
+ private fun showLoadingView(isVisible: Boolean) {
+ with(binding) {
+ loadingView.isVisible = isVisible
+ sendButton.isEnabled = !isVisible
+ }
+ }
+
+ private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
+ val errorDialogBuilder = AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(error.errorMessage)
+ .setPositiveButton(R.string.android_button_ok) { _, _ -> }
+ val errorCode = error.getErrorCode(addErrorCode)
+ val errorCodeView = layoutInflater.inflate(R.layout.view_dialog_error_code, view as ViewGroup?, false) as TextView
+ errorCodeView.text = errorCode
+ errorDialogBuilder.setView(errorCodeView)
+ errorDialogBuilder.show()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
index c6153c94e..5eab4b8e0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
@@ -6,6 +6,8 @@ import androidx.appcompat.app.AlertDialog
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.storage.DiaryStorage
import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
@@ -39,7 +41,7 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
return
}
- informViewModel.authenticateInputAndGetDP3TAccessToken(authCode).observe(viewLifecycleOwner) {
+ informViewModel.authenticateCovidcode(authCode).observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
setLoadingViewVisible(true)
@@ -52,9 +54,7 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
}
setSendButtonEnabled(true)
}
- Status.SUCCESS -> {
- it.data?.let { accessToken -> informExposed(accessToken) }
- }
+ Status.SUCCESS -> informExposed()
}
}
}
@@ -68,8 +68,8 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
throwable.printStackTrace()
}
- private fun informExposed(accessToken: String) {
- informViewModel.informExposed(accessToken, requireActivity()).observe(viewLifecycleOwner) {
+ private fun informExposed() {
+ informViewModel.informExposed(requireActivity()).observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
setLoadingViewVisible(true)
@@ -84,7 +84,13 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
}
Status.SUCCESS -> {
setLoadingViewVisible(false)
- showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ context?.let { context ->
+ if (DiaryStorage.getInstance(context).entries.isEmpty()) {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ } else {
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ }
+ }
setSendButtonEnabled(true)
}
}
diff --git a/app/src/main/res/layout/fragment_share_checkins.xml b/app/src/main/res/layout/fragment_share_checkins.xml
index fd7dfab97..c0548f06d 100644
--- a/app/src/main/res/layout/fragment_share_checkins.xml
+++ b/app/src/main/res/layout/fragment_share_checkins.xml
@@ -1,79 +1,99 @@
-
-
-
-
-
+ android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/spacing_medium_large"
+ android:paddingBottom="@dimen/spacing_larger" />
-
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginVertical="@dimen/spacing_medium_large"
+ android:text="@string/inform_send_button_title" />
+
+
-
+ android:layout_height="match_parent"
+ android:background="@color/transparent_milk"
+ android:visibility="gone">
-
+
-
+
-
\ No newline at end of file
+
From 43c5e81256d74b3f38558975e0907e6b66e3c612 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 14 May 2021 00:08:00 +0200
Subject: [PATCH 071/207] Implemented first version of Upload at the end of
Inform flow
---
.../admin/bag/dp3t/inform/InformFragment.kt | 71 +++++++++----
.../admin/bag/dp3t/inform/InformViewModel.kt | 86 ++++++++++-----
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 30 ++++--
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 52 +++------
.../dp3t/inform/TraceKeyShareBaseFragment.kt | 100 +++++++-----------
.../ch/admin/bag/dp3t/util/AssetUtil.java | 2 +-
app/src/main/res/layout/fragment_inform.xml | 70 ++++++------
7 files changed, 213 insertions(+), 198 deletions(-)
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 9fee26a5d..d57b5e800 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
@@ -25,8 +25,6 @@ private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS +
class InformFragment : TraceKeyShareBaseFragment() {
- private lateinit var binding: FragmentInformBinding
-
companion object {
private const val TAG = "InformFragment"
@@ -34,12 +32,23 @@ class InformFragment : TraceKeyShareBaseFragment() {
fun newInstance() = InformFragment()
}
+ private lateinit var binding: FragmentInformBinding
+
+ override fun onResume() {
+ super.onResume()
+ binding.covidcodeInput.requestFocus()
+ }
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentInformBinding.inflate(inflater).apply {
(requireActivity() as InformActivity).allowBackButton(true)
covidcodeInput.addTextChangedListener(object : ChainedEditTextListener {
override fun onTextChanged(input: String) {
- sendButton.isEnabled = input.matches(REGEX_CODE_PATTERN.toRegex())
+ val matchesRegex = input.matches(REGEX_CODE_PATTERN.toRegex())
+ val matchesChecksum = input.matchesChecksum()
+
+ sendButton.isEnabled = matchesRegex && matchesChecksum
+ setInvalidCovidcodeErrorVisible(matchesRegex && !matchesChecksum)
}
override fun onEditorSendAction() {
@@ -57,12 +66,7 @@ class InformFragment : TraceKeyShareBaseFragment() {
covidcodeInput.text = covidCode
}
}
- sendButton.setOnClickListener {
- sendButton.isEnabled = false
- setInvalidCodeErrorVisible(false)
- val authCode = covidcodeInput.text
- authenticateInputAndInformExposed(authCode)
- }
+ sendButton.setOnClickListener { performContinueAction() }
cancelButton.setOnClickListener { requireActivity().finish() }
informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
}
@@ -70,30 +74,51 @@ class InformFragment : TraceKeyShareBaseFragment() {
return binding.root
}
+ private fun performContinueAction() {
+ binding.sendButton.isEnabled = false
+ setInvalidCovidcodeErrorVisible(false)
+ informViewModel.covidCode = binding.covidcodeInput.text
+ askUserToEnableTracingIfNecessary { tracingEnabled ->
+ if (tracingEnabled) {
+ showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
+ } else {
+ binding.sendButton.isEnabled = true
+ }
+ }
+ }
+
+ private fun onUserGrantedTEKSharing() {
+ informViewModel.hasSharedDP3TKeys = true
+ if (informViewModel.selectableCheckinItems.isEmpty()) {
+ performUpload(onSuccess = {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ }, onInvalidCovidCode = {
+ setInvalidCovidcodeErrorVisible(true)
+ })
+ } else {
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ }
+ }
- override fun onResume() {
- super.onResume()
- binding.covidcodeInput.requestFocus()
+ private fun onUserDidNotGrantTEKSharing() {
+ showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
+ private fun String.matchesChecksum(): Boolean {
+ //TODO: Implement checksum check
+ return true
+ }
override fun setLoadingViewVisible(isVisible: Boolean) {
binding.loadingView.isVisible = isVisible
+ binding.sendButton.isEnabled = !isVisible
}
- override fun setSendButtonEnabled(isEnabled: Boolean) {
- binding.sendButton.isEnabled = isEnabled
- }
-
- override fun setInvalidCodeErrorVisible(visible: Boolean) {
+ private fun setInvalidCovidcodeErrorVisible(isVisible: Boolean) {
binding.apply {
- informInvalidCodeError.isVisible = visible
- informInputText.isVisible = !visible
+ informInvalidCodeError.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+ informInputText.visibility = if (!isVisible) View.VISIBLE else View.INVISIBLE
}
}
- override fun performNotShareAction() {
- showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 00b114aca..3a4cae663 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -1,6 +1,5 @@
package ch.admin.bag.dp3t.inform
-import android.app.Activity
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
@@ -11,17 +10,22 @@ import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.inform.models.Resource
import ch.admin.bag.dp3t.inform.models.SelectableCheckinItem
import ch.admin.bag.dp3t.networking.AuthCodeRepository
+import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
+import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.JwtUtil
import ch.admin.bag.dp3t.util.toUploadVenueInfo
+import com.google.android.gms.common.api.ApiException
import kotlinx.coroutines.Dispatchers
import org.crowdnotifier.android.sdk.CrowdNotifier
import org.dpppt.android.sdk.DP3T
import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.models.DayDate
import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization
+import retrofit2.HttpException
import java.util.*
+import java.util.concurrent.CancellationException
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -30,7 +34,9 @@ private const val USER_UPLOAD_VERSION = 3
private const val TIMEOUT_VALID_CODE = 1000L * 60 * 5
private const val MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L
private const val ISOLATION_DURATION_DAYS = 14L
+private const val KEY_COVIDCODE = "KEY_COVIDCODE"
private const val KEY_HAS_SHARED_DP3T_KEYS = "KEY_HAS_SHARED_DP3T_KEYS"
+private const val KEY_HAS_SHARED_CHECKINS = "KEY_HAS_SHARED_CHECKINS"
class InformViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) {
@@ -40,18 +46,57 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private val secureStorage = SecureStorage.getInstance(application)
var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
+
+ var covidCode: String
+ get() = state.get(KEY_COVIDCODE) ?: ""
+ set(value) = state.set(KEY_COVIDCODE, value)
+
var hasSharedDP3TKeys: Boolean
get() = state.get(KEY_HAS_SHARED_DP3T_KEYS) ?: false
set(value) = state.set(KEY_HAS_SHARED_DP3T_KEYS, value)
- fun userUpload() = liveData(Dispatchers.IO) {
+ var hasSharedCheckins: Boolean
+ get() = state.get(KEY_HAS_SHARED_CHECKINS) ?: false
+ set(value) = state.set(KEY_HAS_SHARED_CHECKINS, value)
+
+ fun performUpload() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
- val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
- emit(Resource.success(data = userUploadRepository.userUpload(getUserUploadPayload(), authorizationHeader)))
- } catch (exception: Exception) {
- emit(Resource.error(data = null, exception = exception))
+ loadAccessTokens(covidCode)
+ } catch (exception: Throwable) {
+ when (exception) {
+ is InvalidCodeError -> emit(Resource.error(InformRequestError.BLACK_INVALID_AUTH_RESPONSE_FORM, exception))
+ is ResponseError -> emit(Resource.error(InformRequestError.BLACK_STATUS_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.BLACK_MISC_NETWORK_ERROR, exception))
+ }
+ return@liveData
}
+ try {
+ if (hasSharedDP3TKeys) {
+ uploadTEKs()
+ }
+ } catch (exception: Throwable) {
+ when (exception) {
+ is ResponseError -> emit(Resource.error(InformRequestError.RED_STATUS_ERROR, exception))
+ is CancellationException -> emit(Resource.error(InformRequestError.RED_USER_CANCELLED_SHARE, exception))
+ is ApiException -> emit(Resource.error(InformRequestError.RED_EXPOSURE_API_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.RED_MISC_NETWORK_ERROR, exception))
+ }
+ return@liveData
+ }
+ try {
+ if (hasSharedCheckins) {
+ val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
+ userUploadRepository.userUpload(getUserUploadPayload(), authorizationHeader)
+ }
+ } catch (exception: Throwable) {
+ when (exception) {
+ is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, exception))
+ }
+ return@liveData
+ }
+ emit(Resource.success(data = null))
}
fun getLastCovidcode(): String? {
@@ -64,25 +109,15 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
}
- fun authenticateCovidcode(covidcode: String) = liveData(Dispatchers.IO) {
- emit(Resource.loading(data = null))
- try {
- emit(Resource.success(loadAccessTokens(covidcode)))
- } catch (exception: Throwable) {
- emit(Resource.error(data = null, exception = exception))
- }
- }
-
- fun informExposed(activity: Activity) = liveData(Dispatchers.IO) {
- emit(Resource.loading(data = null))
+ private suspend fun uploadTEKs() {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastDP3TInformToken)
val onsetDate = JwtUtil.getOnsetDate(secureStorage.lastDP3TInformToken)
- var result: Resource? = null
+ var error: Throwable? = null
// Wrapping traditional callback in a suspendCoroutine
suspendCoroutine { continuation ->
- DP3T.sendIAmInfected(activity, onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
+ DP3T.uploadTEKs(getApplication(), onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
object : ResponseCallback {
override fun onSuccess(oldestSharedKeyDayDate: DayDate) {
@@ -97,18 +132,17 @@ class InformViewModel(application: Application, private val state: SavedStateHan
val isolationEndDialogTimestamp =
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(ISOLATION_DURATION_DAYS)
secureStorage.isolationEndDialogTimestamp = isolationEndDialogTimestamp
- result = Resource.success(Unit)
hasSharedDP3TKeys = true
continuation.resume(Unit)
}
override fun onError(throwable: Throwable) {
- result = Resource.error(null, throwable)
+ error = throwable
continuation.resume(Unit)
}
})
}
- result?.let { emit(it) }
+ error?.let { throw(it) }
}
private suspend fun loadAccessTokens(covidcode: String) {
@@ -123,14 +157,8 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
}
- fun filterSelectableDiaryItems() {
- selectableCheckinItems = selectableCheckinItems.filter {
- Date(it.diaryEntry.departureTime).after(JwtUtil.getOnsetDate(secureStorage.lastCheckinInformToken))
- }
- }
-
-
private fun getUserUploadPayload(): UserUploadPayload {
+ //TODO: Pad with fake Payloads
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
selectableCheckinItems.filter {
it.isSelected
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index 2f82167d3..2931340ee 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -22,7 +22,8 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
binding = FragmentInformReallyNotShareBinding.inflate(inflater).apply {
(requireActivity() as InformActivity).allowBackButton(false)
tryAgainButton.setOnClickListener {
- informViewModel.getLastCovidcode()?.let { authenticateInputAndInformExposed(it) }
+ tryAgainButton.isEnabled = false
+ showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
}
dontSendButton.setOnClickListener {
if (DiaryStorage.getInstance(requireContext()).entries.isNotEmpty()) {
@@ -35,20 +36,27 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
return binding.root
}
- override fun setLoadingViewVisible(isVisible: Boolean) {
- binding.loadingView.isVisible = isVisible
- }
-
- override fun setSendButtonEnabled(isEnabled: Boolean) {
- binding.tryAgainButton.isEnabled = isEnabled
+ private fun onUserGrantedTEKSharing() {
+ informViewModel.hasSharedDP3TKeys = true
+ if (informViewModel.selectableCheckinItems.isEmpty()) {
+ performUpload(
+ onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) },
+ onInvalidCovidCode = {
+ //TODO: Handle Invalid Covidcode
+ })
+ } else {
+ showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
+ }
}
- override fun setInvalidCodeErrorVisible(isVisible: Boolean) {
- // The Code must be valid in this fragment otherwise we cannot get here
+ private fun onUserDidNotGrantTEKSharing() {
+ binding.tryAgainButton.isEnabled = true
+ showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
- override fun performNotShareAction() {
- // stay on this screen
+ override fun setLoadingViewVisible(isVisible: Boolean) {
+ binding.loadingView.isVisible = isVisible
+ binding.tryAgainButton.isEnabled = !isVisible
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index 7dbf3af42..57dce354e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -17,20 +17,17 @@ import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.util.showFragment
import retrofit2.HttpException
-class ShareCheckinsFragment : Fragment() {
+class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
companion object {
@JvmStatic
fun newInstance() = ShareCheckinsFragment()
}
- private val informViewModel: InformViewModel by activityViewModels()
-
private lateinit var binding: FragmentShareCheckinsBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentShareCheckinsBinding.inflate(inflater).apply {
- informViewModel.filterSelectableDiaryItems()
val adapter = CheckinAdapter()
checkinsRecyclerView.adapter = adapter
checkinsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
@@ -45,54 +42,33 @@ class ShareCheckinsFragment : Fragment() {
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
if (informViewModel.hasSharedDP3TKeys) {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ upload()
} else {
//TODO: Show "Thanks for nothing" Fragment
}
}
- sendButton.setOnClickListener { performUserUpload() }
+ sendButton.setOnClickListener {
+ informViewModel.hasSharedCheckins = true
+ upload()
+ }
}
return binding.root
}
- private fun performUserUpload() {
-
- informViewModel.userUpload().observe(viewLifecycleOwner) {
- binding.loadingView.isVisible = it.status == Status.LOADING
- when (it.status) {
- Status.LOADING -> showLoadingView(true)
- Status.ERROR -> {
- showLoadingView(false)
- if (it.exception is HttpException) {
- showErrorDialog(InformRequestError.USER_UPLOAD_NETWORK_ERROR, it.exception.code().toString())
- } else {
- showErrorDialog(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, it.exception?.message)
- }
- }
- Status.SUCCESS -> {
- showLoadingView(false)
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- }
- }
- }
+ private fun upload() {
+ performUpload(onSuccess = {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ }, onInvalidCovidCode = {
+ //TODO: Handle invalid covidcode
+ })
}
- private fun showLoadingView(isVisible: Boolean) {
+ override fun setLoadingViewVisible(isVisible: Boolean) {
with(binding) {
loadingView.isVisible = isVisible
sendButton.isEnabled = !isVisible
+ dontSendButton.isEnabled = !isVisible
}
}
- private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
- val errorDialogBuilder = AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(error.errorMessage)
- .setPositiveButton(R.string.android_button_ok) { _, _ -> }
- val errorCode = error.getErrorCode(addErrorCode)
- val errorCodeView = layoutInflater.inflate(R.layout.view_dialog_error_code, view as ViewGroup?, false) as TextView
- errorCodeView.text = errorCode
- errorDialogBuilder.setView(errorCodeView)
- errorDialogBuilder.show()
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
index 5eab4b8e0..286b03d83 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
@@ -6,18 +6,16 @@ import androidx.appcompat.app.AlertDialog
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.storage.DiaryStorage
+import ch.admin.bag.dp3t.inform.models.Resource
import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.util.ENExceptionHelper
-import ch.admin.bag.dp3t.util.showFragment
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import com.google.android.gms.common.api.ApiException
import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.internal.logger.Logger
-import java.util.concurrent.CancellationException
abstract class TraceKeyShareBaseFragment : Fragment() {
@@ -30,81 +28,55 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
private val tracingViewModel: TracingViewModel by activityViewModels()
abstract fun setLoadingViewVisible(isVisible: Boolean)
- abstract fun setSendButtonEnabled(isEnabled: Boolean)
- abstract fun setInvalidCodeErrorVisible(isVisible: Boolean)
- abstract fun performNotShareAction()
- protected fun authenticateInputAndInformExposed(authCode: String) {
- val isTracingEnabled = DP3T.isTracingEnabled(requireContext())
- if (!isTracingEnabled) {
- askUserToEnableTracing(authCode)
- return
- }
+ protected fun showShareTEKsPopup(onSuccess: () -> Unit, onError: () -> Unit) {
+ DP3T.showShareTEKsPopup(requireActivity(), object : ResponseCallback {
+ override fun onSuccess(p0: Void?) {
+ onSuccess()
+ }
- informViewModel.authenticateCovidcode(authCode).observe(viewLifecycleOwner) {
- when (it.status) {
- Status.LOADING -> {
- setLoadingViewVisible(true)
- setSendButtonEnabled(false)
- }
- Status.ERROR -> {
- setLoadingViewVisible(false)
- it.exception?.let { exception ->
- handleAuthenticateRequestError(exception)
- }
- setSendButtonEnabled(true)
- }
- Status.SUCCESS -> informExposed()
+ override fun onError(p0: Throwable?) {
+ onError()
}
- }
+ })
}
- private fun handleAuthenticateRequestError(throwable: Throwable) {
- when (throwable) {
- is InvalidCodeError -> setInvalidCodeErrorVisible(true)
- is ResponseError -> showErrorDialog(InformRequestError.BLACK_STATUS_ERROR, throwable.statusCode.toString())
- else -> showErrorDialog(InformRequestError.BLACK_MISC_NETWORK_ERROR)
+ protected fun askUserToEnableTracingIfNecessary(continuation: (isEnabled: Boolean) -> Unit) {
+ val isTracingEnabled = DP3T.isTracingEnabled(requireContext())
+ if (!isTracingEnabled) {
+ showEnableTracingDialog(continuation)
+ } else {
+ continuation(true)
}
- throwable.printStackTrace()
}
- private fun informExposed() {
- informViewModel.informExposed(requireActivity()).observe(viewLifecycleOwner) {
+ protected fun performUpload(onSuccess: () -> Unit, onInvalidCovidCode: () -> Unit) {
+ informViewModel.performUpload().observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
setLoadingViewVisible(true)
- setSendButtonEnabled(false)
}
Status.ERROR -> {
setLoadingViewVisible(false)
- it.exception?.let { exception ->
- handleInformExposedRequestError(exception)
- }
- setSendButtonEnabled(true)
+ handleUploadException(it, onInvalidCovidCode)
}
Status.SUCCESS -> {
setLoadingViewVisible(false)
- context?.let { context ->
- if (DiaryStorage.getInstance(context).entries.isEmpty()) {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- } else {
- showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
- }
- }
- setSendButtonEnabled(true)
+ onSuccess()
}
}
}
}
- private fun handleInformExposedRequestError(throwable: Throwable) {
- when (throwable) {
- is ResponseError -> showErrorDialog(InformRequestError.RED_STATUS_ERROR, throwable.statusCode.toString())
- is CancellationException -> performNotShareAction()
- is ApiException -> showErrorDialog(InformRequestError.RED_EXPOSURE_API_ERROR, throwable.statusCode.toString())
- else -> showErrorDialog(InformRequestError.RED_MISC_NETWORK_ERROR)
+ private fun handleUploadException(error: Resource, onInvalidCovidCode: () -> Unit) {
+ if (error.data == null) return
+ when (error.exception) {
+ is InvalidCodeError -> onInvalidCovidCode()
+ is ResponseError -> showErrorDialog(error.data, error.exception.statusCode.toString())
+ is ApiException -> showErrorDialog(error.data, error.exception.statusCode.toString())
+ else -> showErrorDialog(error.data)
}
- throwable.printStackTrace()
+ error.exception?.printStackTrace()
}
private fun showErrorDialog(error: InformRequestError, addErrorCode: String? = null) {
@@ -118,29 +90,29 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
errorDialogBuilder.show()
}
- private fun askUserToEnableTracing(authCode: String) {
+ private fun showEnableTracingDialog(continuation: (isEnabled: Boolean) -> Unit) {
AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
.setMessage(R.string.android_inform_tracing_enabled_explanation)
- .setOnCancelListener { setSendButtonEnabled(true) }
- .setNegativeButton(R.string.cancel) { _, _ -> setSendButtonEnabled(true) }
- .setPositiveButton(R.string.activate_tracing_button) { _, _ -> enableTracing(authCode) }
+ .setOnCancelListener { continuation(false) }
+ .setNegativeButton(R.string.cancel) { _, _ -> continuation(false) }
+ .setPositiveButton(R.string.activate_tracing_button) { _, _ -> enableTracing(continuation) }
.show()
}
- private fun enableTracing(authCode: String) {
+ private fun enableTracing(continuation: (isEnabled: Boolean) -> Unit) {
tracingViewModel.enableTracing(requireActivity(),
- { authenticateInputAndInformExposed(authCode) },
+ { continuation(true) },
{ e: Exception? ->
val message = ENExceptionHelper.getErrorMessage(e, activity)
Logger.e(TAG, message)
AlertDialog.Builder(requireActivity(), R.style.NextStep_AlertDialogStyle)
.setTitle(R.string.android_en_start_failure)
.setMessage(message)
- .setOnDismissListener { setSendButtonEnabled(true) }
+ .setOnDismissListener { continuation(false) }
.setPositiveButton(R.string.android_button_ok) { _, _ -> }
.show()
}
- ) { setSendButtonEnabled(true) }
+ ) { continuation(false) }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/AssetUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/AssetUtil.java
index 550e904cd..1c2383cc0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/AssetUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/AssetUtil.java
@@ -91,7 +91,7 @@ public static String loadImpressumHtmlFile(Context context, String filename) {
String impressum = html.toString();
StringBuilder versionString = new StringBuilder(BuildConfig.VERSION_NAME)
.append(", ")
- .append(org.dpppt.android.sdk.BuildConfig.VERSION_NAME);
+ .append(org.dpppt.android.sdk.BuildConfig.LIBRARY_VERSION_NAME);
StringBuilder buildString =
new StringBuilder(String.valueOf(BuildConfig.BUILD_TIME))
.append(" / ")
diff --git a/app/src/main/res/layout/fragment_inform.xml b/app/src/main/res/layout/fragment_inform.xml
index 29ef7ee8e..618ea4086 100644
--- a/app/src/main/res/layout/fragment_inform.xml
+++ b/app/src/main/res/layout/fragment_inform.xml
@@ -46,45 +46,51 @@
android:gravity="bottom|center_horizontal"
android:text="@string/inform_code_title" />
-
+
-
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="@dimen/spacing_very_small"
+ android:background="?selectableItemBackground"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:padding="@dimen/spacing_medium"
+ android:visibility="invisible"
+ tools:visibility="visible">
+
+
+
+
+
+
-
-
+ android:paddingStart="@dimen/spacing_huge"
+ android:paddingEnd="@dimen/spacing_huge"
+ android:text="@string/inform_code_text" />
-
+
+ android:text="@string/inform_continue_button" />
From 49c372cc70029d823b4546819d7538b3c3aa898d Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 17 May 2021 09:31:22 +0200
Subject: [PATCH 072/207] Implemented Wrong Covidcode with correct checksum
case
---
.../admin/bag/dp3t/inform/InformFragment.kt | 43 +++++++++++++------
.../bag/dp3t/inform/InformIntroFragment.java | 2 +-
.../admin/bag/dp3t/inform/InformViewModel.kt | 8 ++--
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 5 ++-
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 9 +---
5 files changed, 39 insertions(+), 28 deletions(-)
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 d57b5e800..368b7dbf6 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
@@ -13,6 +13,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentInformBinding
@@ -22,6 +23,7 @@ import ch.admin.bag.dp3t.util.PhoneUtil
import ch.admin.bag.dp3t.util.showFragment
private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}"
+private const val ARG_IS_COVID_CODE_INVALID_CASE = "ARG_IS_COVID_CODE_INVALID_CASE"
class InformFragment : TraceKeyShareBaseFragment() {
@@ -29,7 +31,9 @@ class InformFragment : TraceKeyShareBaseFragment() {
private const val TAG = "InformFragment"
@JvmStatic
- fun newInstance() = InformFragment()
+ fun newInstance(isCovidCodeInvalidCase: Boolean = false) = InformFragment().apply {
+ arguments = bundleOf(ARG_IS_COVID_CODE_INVALID_CASE to isCovidCodeInvalidCase)
+ }
}
private lateinit var binding: FragmentInformBinding
@@ -40,7 +44,8 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- binding = FragmentInformBinding.inflate(inflater).apply {
+ binding = FragmentInformBinding.inflate(inflater)
+ return binding.apply {
(requireActivity() as InformActivity).allowBackButton(true)
covidcodeInput.addTextChangedListener(object : ChainedEditTextListener {
override fun onTextChanged(input: String) {
@@ -56,9 +61,7 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
})
- informViewModel.getLastCovidcode()?.let {
- covidcodeInput.text = it
- }
+ covidcodeInput.text = informViewModel.covidCode
if (requireActivity().intent.extras != null) {
val covidCode = requireActivity().intent.extras?.getString(InformActivity.EXTRA_COVIDCODE)
@@ -66,12 +69,16 @@ class InformFragment : TraceKeyShareBaseFragment() {
covidcodeInput.text = covidCode
}
}
+
+ if (arguments?.getBoolean(ARG_IS_COVID_CODE_INVALID_CASE) == true) {
+ binding.sendButton.setText(R.string.inform_send_button_title)
+ setInvalidCovidcodeErrorVisible(true)
+ }
+
sendButton.setOnClickListener { performContinueAction() }
cancelButton.setOnClickListener { requireActivity().finish() }
informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
- }
-
- return binding.root
+ }.root
}
private fun performContinueAction() {
@@ -80,7 +87,11 @@ class InformFragment : TraceKeyShareBaseFragment() {
informViewModel.covidCode = binding.covidcodeInput.text
askUserToEnableTracingIfNecessary { tracingEnabled ->
if (tracingEnabled) {
- showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
+ if (arguments?.getBoolean(ARG_IS_COVID_CODE_INVALID_CASE) == true) {
+ performUpload()
+ } else {
+ showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
+ }
} else {
binding.sendButton.isEnabled = true
}
@@ -90,16 +101,20 @@ class InformFragment : TraceKeyShareBaseFragment() {
private fun onUserGrantedTEKSharing() {
informViewModel.hasSharedDP3TKeys = true
if (informViewModel.selectableCheckinItems.isEmpty()) {
- performUpload(onSuccess = {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- }, onInvalidCovidCode = {
- setInvalidCovidcodeErrorVisible(true)
- })
+ performUpload()
} else {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
}
}
+ private fun performUpload() {
+ performUpload(onSuccess = {
+ showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
+ }, onInvalidCovidCode = {
+ setInvalidCovidcodeErrorVisible(true)
+ })
+ }
+
private fun onUserDidNotGrantTEKSharing() {
showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
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 58ad7293b..d843dc6ed 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
@@ -58,7 +58,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
getParentFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.inform_fragment_container, InformFragment.newInstance())
+ .replace(R.id.inform_fragment_container, InformFragment.newInstance(false))
.addToBackStack(InformFragment.class.getCanonicalName())
.commit();
});
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 3a4cae663..92e6fffae 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -48,7 +48,7 @@ class InformViewModel(application: Application, private val state: SavedStateHan
var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
var covidCode: String
- get() = state.get(KEY_COVIDCODE) ?: ""
+ get() = state.get(KEY_COVIDCODE) ?: getLastCovidcode()
set(value) = state.set(KEY_COVIDCODE, value)
var hasSharedDP3TKeys: Boolean
@@ -99,13 +99,13 @@ class InformViewModel(application: Application, private val state: SavedStateHan
emit(Resource.success(data = null))
}
- fun getLastCovidcode(): String? {
+ private fun getLastCovidcode(): String {
val lastCovidcode = secureStorage.lastInformCode
return if (System.currentTimeMillis() - secureStorage.lastInformRequestTime < TIMEOUT_VALID_CODE) {
- lastCovidcode
+ lastCovidcode ?: ""
} else {
- null
+ ""
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index 2931340ee..64757168b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -1,5 +1,6 @@
package ch.admin.bag.dp3t.inform
+import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -25,6 +26,7 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
tryAgainButton.isEnabled = false
showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
}
+ dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
if (DiaryStorage.getInstance(requireContext()).entries.isNotEmpty()) {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
@@ -42,7 +44,7 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
performUpload(
onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) },
onInvalidCovidCode = {
- //TODO: Handle Invalid Covidcode
+ showFragment(InformFragment.newInstance(isCovidCodeInvalidCase = true), R.id.inform_fragment_container)
})
} else {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
@@ -51,7 +53,6 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
private fun onUserDidNotGrantTEKSharing() {
binding.tryAgainButton.isEnabled = true
- showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
override fun setLoadingViewVisible(isVisible: Boolean) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index 57dce354e..e6b06b087 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -5,17 +5,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
-import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentShareCheckinsBinding
-import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.util.showFragment
-import retrofit2.HttpException
class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
@@ -28,6 +22,7 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentShareCheckinsBinding.inflate(inflater).apply {
+ (requireActivity() as InformActivity).allowBackButton(false)
val adapter = CheckinAdapter()
checkinsRecyclerView.adapter = adapter
checkinsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
@@ -59,7 +54,7 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
performUpload(onSuccess = {
showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
}, onInvalidCovidCode = {
- //TODO: Handle invalid covidcode
+ showFragment(InformFragment.newInstance(isCovidCodeInvalidCase = true), R.id.inform_fragment_container)
})
}
From d76ca27fe6ff0246d4ff3236aa9be30b7c0a5ac6 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 17 May 2021 09:47:02 +0200
Subject: [PATCH 073/207] Added Checkin part to ThankYou fragment
---
.../bag/dp3t/inform/ThankYouFragment.java | 77 -------------------
.../admin/bag/dp3t/inform/ThankYouFragment.kt | 63 +++++++++++++++
.../main/res/layout/fragment_thank_you.xml | 16 +++-
3 files changed, 76 insertions(+), 80 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.java b/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.java
deleted file mode 100644
index 53daa1500..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.inform;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import java.util.TimeZone;
-
-import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.storage.SecureStorage;
-import ch.admin.bag.dp3t.util.DateUtils;
-
-public class ThankYouFragment extends Fragment {
-
- private SecureStorage secureStorage;
-
- public static ThankYouFragment newInstance() {
- return new ThankYouFragment();
- }
-
- public ThankYouFragment() {
- super(R.layout.fragment_thank_you);
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- secureStorage = SecureStorage.getInstance(getContext());
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- ((InformActivity) requireActivity()).allowBackButton(false);
-
- // Show the onset date in the thank you message
- TextView thankYouInfoTextView = view.findViewById(R.id.inform_thank_you_text_info);
- TextView onsetDateTextView = view.findViewById(R.id.inform_thank_you_text_onsetdate);
- TextView stopInfectionChainsTextView = view.findViewById(R.id.inform_thank_you_text_stop_infection_chains);
-
- long oldestSharedKeyDateMillis = secureStorage.getPositiveReportOldestSharedKey();
- if (oldestSharedKeyDateMillis > 0L) {
- String formattedDate = DateUtils.getFormattedDateWrittenMonth(oldestSharedKeyDateMillis, TimeZone.getTimeZone("UTC"));
- String formattedOnsetDateText =
- getString(R.string.inform_send_thankyou_text_onsetdate).replace("{ONSET_DATE}", formattedDate);
-
- thankYouInfoTextView.setText(R.string.inform_send_thankyou_text_onsetdate_info);
- onsetDateTextView.setText(formattedOnsetDateText);
- stopInfectionChainsTextView.setText(R.string.inform_send_thankyou_text_stop_infection_chains);
- } else {
- thankYouInfoTextView.setText(R.string.inform_send_thankyou_text);
- onsetDateTextView.setVisibility(View.GONE);
- stopInfectionChainsTextView.setVisibility(View.GONE);
- }
-
- view.findViewById(R.id.inform_thank_you_button_continue).setOnClickListener(v -> {
- getParentFragmentManager().beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.inform_fragment_container, TracingStoppedFragment.newInstance())
- .commit();
- });
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
new file mode 100644
index 000000000..815447238
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Ubique Innovation AG
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package ch.admin.bag.dp3t.inform
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+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.databinding.FragmentThankYouBinding
+import ch.admin.bag.dp3t.storage.SecureStorage
+import ch.admin.bag.dp3t.util.DateUtils
+import ch.admin.bag.dp3t.util.showFragment
+import java.util.*
+
+class ThankYouFragment : Fragment() {
+
+ companion object {
+ fun newInstance() = ThankYouFragment()
+ }
+
+ private val informViewModel: InformViewModel by activityViewModels()
+
+ private val secureStorage by lazy { SecureStorage.getInstance(requireContext()) }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return FragmentThankYouBinding.inflate(inflater).apply {
+ (requireActivity() as InformActivity).allowBackButton(false)
+
+ // Show the onset date in the thank you message
+ val oldestSharedKeyDateMillis = secureStorage.positiveReportOldestSharedKey
+ if (oldestSharedKeyDateMillis > 0L) {
+ val formattedDate = DateUtils.getFormattedDateWrittenMonth(oldestSharedKeyDateMillis, TimeZone.getTimeZone("UTC"))
+ val formattedOnsetDateText =
+ getString(R.string.inform_send_thankyou_text_onsetdate).replace("{ONSET_DATE}", formattedDate)
+ informThankYouTextInfo.setText(R.string.inform_send_thankyou_text_onsetdate_info)
+ informThankYouTextOnsetdate.text = formattedOnsetDateText
+ informThankYouTextStopInfectionChains.setText(R.string.inform_send_thankyou_text_stop_infection_chains)
+ } else {
+ informThankYouTextInfo.setText(R.string.inform_send_thankyou_text)
+ informThankYouTextOnsetdate.visibility = View.GONE
+ informThankYouTextStopInfectionChains.visibility = View.GONE
+ }
+
+ informThankYouTextCheckins.isVisible = informViewModel.hasSharedCheckins
+
+ informThankYouButtonContinue.setOnClickListener {
+ showFragment(TracingStoppedFragment.newInstance(), R.id.inform_fragment_container)
+ }
+ }.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_thank_you.xml b/app/src/main/res/layout/fragment_thank_you.xml
index 20e34c343..aea5e8bd1 100644
--- a/app/src/main/res/layout/fragment_thank_you.xml
+++ b/app/src/main/res/layout/fragment_thank_you.xml
@@ -8,10 +8,10 @@
~ SPDX-License-Identifier: MPL-2.0
-->
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white">
+
+
Date: Thu, 20 May 2021 21:23:41 +0200
Subject: [PATCH 074/207] Added NotThankYou Fragment
---
app/build.gradle | 2 +-
.../bag/dp3t/extensions/FragmentExtensions.kt | 13 +--
.../ch/admin/bag/dp3t/home/HomeFragment.java | 4 +-
.../admin/bag/dp3t/inform/CheckinAdapter.kt | 7 +-
.../admin/bag/dp3t/inform/InformFragment.kt | 37 ++-----
.../bag/dp3t/inform/InformIntroFragment.java | 2 +-
.../admin/bag/dp3t/inform/InformViewModel.kt | 73 +++++++++----
.../bag/dp3t/inform/NotThankYouFragment.kt | 27 +++++
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 10 +-
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 11 +-
.../admin/bag/dp3t/inform/ThankYouFragment.kt | 2 +-
.../dp3t/inform/TraceKeyShareBaseFragment.kt | 14 +--
.../main/res/drawable/illu_not_thank_you.xml | 80 ++++++++++++++
.../res/layout/fragment_not_thank_you.xml | 100 ++++++++++++++++++
.../res/layout/fragment_share_checkins.xml | 49 +++++----
app/src/main/res/values-bs/strings.xml | 11 ++
app/src/main/res/values-de/strings.xml | 16 +++
app/src/main/res/values-es/strings.xml | 11 ++
app/src/main/res/values-fr/strings.xml | 11 ++
app/src/main/res/values-hr/strings.xml | 11 ++
app/src/main/res/values-it/strings.xml | 11 ++
app/src/main/res/values-pt/strings.xml | 11 ++
app/src/main/res/values-rm/strings.xml | 11 ++
app/src/main/res/values-sq/strings.xml | 11 ++
app/src/main/res/values-sr/strings.xml | 11 ++
app/src/main/res/values-ti/strings.xml | 11 ++
app/src/main/res/values-tr/strings.xml | 11 ++
app/src/main/res/values/strings.xml | 11 ++
28 files changed, 470 insertions(+), 109 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
create mode 100644 app/src/main/res/drawable/illu_not_thank_you.xml
create mode 100644 app/src/main/res/layout/fragment_not_thank_you.xml
diff --git a/app/build.gradle b/app/build.gradle
index b31c06d3a..c3302c1b5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -200,7 +200,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
- def dp3t_sdk_version = '2.2.0'
+ def dp3t_sdk_version = '2.3.0-dev-6'
devImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version-calibration"
teschtImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
abnahmeImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
index 9025583c8..64218cc0e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
@@ -7,19 +7,10 @@ import ch.admin.bag.dp3t.R
fun Fragment.showFragment(
fragment: Fragment,
@IdRes container: Int = R.id.main_fragment_container,
- addToBackStack: Boolean = true,
- modalAnimation: Boolean = false
+ addToBackStack: Boolean = true
) {
requireActivity().supportFragmentManager.beginTransaction()
- .apply {
- if (modalAnimation) {
- this.setCustomAnimations(
- R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit
- )
- } else {
- this.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- }
- }
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
.replace(container, fragment)
.apply { if (addToBackStack) this.addToBackStack(fragment::class.java.canonicalName) }
.commit()
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 dafc4dc8e..06f9fe725 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
@@ -258,9 +258,9 @@ private void showReportsFragment() {
int tracingReports = tracingViewModel.getAppStatusLiveData().getValue().getExposureDays().size();
boolean isReportedPositive = tracingViewModel.getTracingStatusInterface().isReportedAsInfected();
if (((checkinReports > 0 && tracingReports > 0) || checkinReports > 1) && !isReportedPositive) {
- FragmentExtensionsKt.showFragment(this, ReportsOverviewFragment.newInstance(), true);
+ FragmentExtensionsKt.showFragment(this, ReportsOverviewFragment.newInstance(), R.id.main_fragment_container, true);
} else {
- FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), true);
+ FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), R.id.main_fragment_container, true);
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
index b09b0b84e..02e024a73 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
@@ -5,7 +5,9 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.databinding.ItemSelectableCheckinBinding
+import ch.admin.bag.dp3t.extensions.getSubtitle
import ch.admin.bag.dp3t.inform.models.SelectableCheckinItem
+import ch.admin.bag.dp3t.util.StringUtil
class CheckinAdapter : RecyclerView.Adapter() {
@@ -26,8 +28,9 @@ class CheckinAdapter : RecyclerView.Adapter()
fun bind(selectableCheckinItem: SelectableCheckinItem) {
binding.apply {
checkinTitle.text = selectableCheckinItem.diaryEntry.venueInfo.title
- checkinDetail1.text = "TODO"
- checkinDetail2.text = "TODO"
+ checkinDetail1.setText(selectableCheckinItem.diaryEntry.venueInfo.getSubtitle())
+ checkinDetail2.text =
+ StringUtil.getReportDateString(selectableCheckinItem.diaryEntry.departureTime, true, true, root.context)
checkbox.isChecked = selectableCheckinItem.isSelected
checkbox.setOnCheckedChangeListener { _, isChecked ->
selectableCheckinItem.isSelected = isChecked
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 368b7dbf6..2836a7a6f 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
@@ -13,17 +13,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentInformBinding
+import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.inform.views.ChainedEditText
import ch.admin.bag.dp3t.inform.views.ChainedEditText.ChainedEditTextListener
import ch.admin.bag.dp3t.util.PhoneUtil
-import ch.admin.bag.dp3t.util.showFragment
private const val REGEX_CODE_PATTERN = "\\d{" + ChainedEditText.NUM_CHARACTERS + "}"
-private const val ARG_IS_COVID_CODE_INVALID_CASE = "ARG_IS_COVID_CODE_INVALID_CASE"
class InformFragment : TraceKeyShareBaseFragment() {
@@ -31,9 +29,7 @@ class InformFragment : TraceKeyShareBaseFragment() {
private const val TAG = "InformFragment"
@JvmStatic
- fun newInstance(isCovidCodeInvalidCase: Boolean = false) = InformFragment().apply {
- arguments = bundleOf(ARG_IS_COVID_CODE_INVALID_CASE to isCovidCodeInvalidCase)
- }
+ fun newInstance() = InformFragment()
}
private lateinit var binding: FragmentInformBinding
@@ -50,10 +46,8 @@ class InformFragment : TraceKeyShareBaseFragment() {
covidcodeInput.addTextChangedListener(object : ChainedEditTextListener {
override fun onTextChanged(input: String) {
val matchesRegex = input.matches(REGEX_CODE_PATTERN.toRegex())
- val matchesChecksum = input.matchesChecksum()
-
- sendButton.isEnabled = matchesRegex && matchesChecksum
- setInvalidCovidcodeErrorVisible(matchesRegex && !matchesChecksum)
+ sendButton.isEnabled = matchesRegex
+ setInvalidCovidcodeErrorVisible(false)
}
override fun onEditorSendAction() {
@@ -70,11 +64,6 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
}
- if (arguments?.getBoolean(ARG_IS_COVID_CODE_INVALID_CASE) == true) {
- binding.sendButton.setText(R.string.inform_send_button_title)
- setInvalidCovidcodeErrorVisible(true)
- }
-
sendButton.setOnClickListener { performContinueAction() }
cancelButton.setOnClickListener { requireActivity().finish() }
informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
@@ -87,11 +76,8 @@ class InformFragment : TraceKeyShareBaseFragment() {
informViewModel.covidCode = binding.covidcodeInput.text
askUserToEnableTracingIfNecessary { tracingEnabled ->
if (tracingEnabled) {
- if (arguments?.getBoolean(ARG_IS_COVID_CODE_INVALID_CASE) == true) {
- performUpload()
- } else {
- showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
- }
+ //TODO: Do onsetDate Request here
+ showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
} else {
binding.sendButton.isEnabled = true
}
@@ -108,22 +94,13 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
private fun performUpload() {
- performUpload(onSuccess = {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- }, onInvalidCovidCode = {
- setInvalidCovidcodeErrorVisible(true)
- })
+ performUpload(onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) })
}
private fun onUserDidNotGrantTEKSharing() {
showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container)
}
- private fun String.matchesChecksum(): Boolean {
- //TODO: Implement checksum check
- return true
- }
-
override fun setLoadingViewVisible(isVisible: Boolean) {
binding.loadingView.isVisible = isVisible
binding.sendButton.isEnabled = !isVisible
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 d843dc6ed..58ad7293b 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
@@ -58,7 +58,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
getParentFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.inform_fragment_container, InformFragment.newInstance(false))
+ .replace(R.id.inform_fragment_container, InformFragment.newInstance())
.addToBackStack(InformFragment.class.getCanonicalName())
.commit();
});
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 92e6fffae..1479b188a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -4,6 +4,7 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.liveData
+import ch.admin.bag.dp3t.checkin.models.UploadVenueInfo
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
@@ -17,9 +18,10 @@ import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.JwtUtil
import ch.admin.bag.dp3t.util.toUploadVenueInfo
import com.google.android.gms.common.api.ApiException
+import com.google.protobuf.ByteString
import kotlinx.coroutines.Dispatchers
import org.crowdnotifier.android.sdk.CrowdNotifier
-import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.PendingUploadTask
import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.models.DayDate
import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization
@@ -37,6 +39,8 @@ private const val ISOLATION_DURATION_DAYS = 14L
private const val KEY_COVIDCODE = "KEY_COVIDCODE"
private const val KEY_HAS_SHARED_DP3T_KEYS = "KEY_HAS_SHARED_DP3T_KEYS"
private const val KEY_HAS_SHARED_CHECKINS = "KEY_HAS_SHARED_CHECKINS"
+private const val KEY_PENDING_UPLOAD_TASK = "KEY_PENDING_UPLOAD_TASK"
+private const val USER_UPLOAD_SIZE = 1000
class InformViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) {
@@ -44,6 +48,7 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private val userUploadRepository = UserUploadRepository()
private val diaryStorage = DiaryStorage.getInstance(application)
private val secureStorage = SecureStorage.getInstance(application)
+ private val random = Random()
var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
@@ -59,6 +64,10 @@ class InformViewModel(application: Application, private val state: SavedStateHan
get() = state.get(KEY_HAS_SHARED_CHECKINS) ?: false
set(value) = state.set(KEY_HAS_SHARED_CHECKINS, value)
+ var pendingUploadTask: PendingUploadTask?
+ get() = state.get(KEY_PENDING_UPLOAD_TASK)
+ set(value) = state.set(KEY_PENDING_UPLOAD_TASK, value)
+
fun performUpload() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
@@ -71,30 +80,31 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
return@liveData
}
- try {
- if (hasSharedDP3TKeys) {
+ if (hasSharedDP3TKeys) {
+ try {
uploadTEKs()
+ } catch (exception: Throwable) {
+ when (exception) {
+ is ResponseError -> emit(Resource.error(InformRequestError.RED_STATUS_ERROR, exception))
+ is CancellationException -> emit(Resource.error(InformRequestError.RED_USER_CANCELLED_SHARE, exception))
+ is ApiException -> emit(Resource.error(InformRequestError.RED_EXPOSURE_API_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.RED_MISC_NETWORK_ERROR, exception))
+ }
+ return@liveData
}
- } catch (exception: Throwable) {
- when (exception) {
- is ResponseError -> emit(Resource.error(InformRequestError.RED_STATUS_ERROR, exception))
- is CancellationException -> emit(Resource.error(InformRequestError.RED_USER_CANCELLED_SHARE, exception))
- is ApiException -> emit(Resource.error(InformRequestError.RED_EXPOSURE_API_ERROR, exception))
- else -> emit(Resource.error(InformRequestError.RED_MISC_NETWORK_ERROR, exception))
- }
- return@liveData
}
- try {
- if (hasSharedCheckins) {
+ if (hasSharedCheckins) {
+ try {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
userUploadRepository.userUpload(getUserUploadPayload(), authorizationHeader)
+
+ } catch (exception: Throwable) {
+ when (exception) {
+ is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, exception))
+ }
+ return@liveData
}
- } catch (exception: Throwable) {
- when (exception) {
- is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
- else -> emit(Resource.error(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, exception))
- }
- return@liveData
}
emit(Resource.success(data = null))
}
@@ -117,7 +127,7 @@ class InformViewModel(application: Application, private val state: SavedStateHan
// Wrapping traditional callback in a suspendCoroutine
suspendCoroutine { continuation ->
- DP3T.uploadTEKs(getApplication(), onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
+ pendingUploadTask?.performUpload(getApplication(), onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
object : ResponseCallback {
override fun onSuccess(oldestSharedKeyDayDate: DayDate) {
@@ -158,7 +168,6 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
private fun getUserUploadPayload(): UserUploadPayload {
- //TODO: Pad with fake Payloads
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
selectableCheckinItems.filter {
it.isSelected
@@ -167,9 +176,31 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}.flatten().forEach {
userUploadPayloadBuilder.addVenueInfos(it.toUploadVenueInfo())
}
+
+ for (i in userUploadPayloadBuilder.venueInfosCount until USER_UPLOAD_SIZE) {
+ userUploadPayloadBuilder.addVenueInfos(getRandomFakeVenueInfo())
+ }
return userUploadPayloadBuilder.build()
}
+ private fun getRandomFakeVenueInfo(): UploadVenueInfo {
+ return UploadVenueInfo.newBuilder()
+ .setPreId(ByteString.copyFrom(getRandomByteArray(32)))
+ .setTimeKey(ByteString.copyFrom(getRandomByteArray(32)))
+ .setNotificationKey(ByteString.copyFrom(getRandomByteArray(32)))
+ .setIntervalStartMs(random.nextLong())
+ .setIntervalEndMs(random.nextLong())
+ .setFake(true)
+ .build()
+
+ }
+
+ private fun getRandomByteArray(size: Int): ByteArray {
+ return ByteArray(size).apply {
+ random.nextBytes(this)
+ }
+ }
+
private fun getAuthorizationHeader(accessToken: String): String {
return "Bearer $accessToken"
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
new file mode 100644
index 000000000..6633ba0e5
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
@@ -0,0 +1,27 @@
+package ch.admin.bag.dp3t.inform
+
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.databinding.FragmentNotThankYouBinding
+import ch.admin.bag.dp3t.extensions.showFragment
+
+class NotThankYouFragment : Fragment() {
+
+ companion object {
+ fun newInstance() = NotThankYouFragment()
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return FragmentNotThankYouBinding.inflate(inflater).apply {
+ (requireActivity() as InformActivity).allowBackButton(false)
+ dontSendButton.setOnClickListener { requireActivity().finish() }
+ dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
+ backButton.setOnClickListener { showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container) }
+ }.root
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index 64757168b..4499d4c0f 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -9,7 +9,7 @@ import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.databinding.FragmentInformReallyNotShareBinding
-import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.extensions.showFragment
class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
@@ -31,7 +31,7 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
if (DiaryStorage.getInstance(requireContext()).entries.isNotEmpty()) {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
} else {
- //TODO: Show "Thanks for nothing" Fragment
+ showFragment(NotThankYouFragment.newInstance(), R.id.inform_fragment_container)
}
}
}
@@ -41,11 +41,7 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
private fun onUserGrantedTEKSharing() {
informViewModel.hasSharedDP3TKeys = true
if (informViewModel.selectableCheckinItems.isEmpty()) {
- performUpload(
- onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) },
- onInvalidCovidCode = {
- showFragment(InformFragment.newInstance(isCovidCodeInvalidCase = true), R.id.inform_fragment_container)
- })
+ performUpload(onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) })
} else {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index e6b06b087..df9d8cf3b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -9,7 +9,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentShareCheckinsBinding
-import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.extensions.showFragment
class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
@@ -25,7 +25,6 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
(requireActivity() as InformActivity).allowBackButton(false)
val adapter = CheckinAdapter()
checkinsRecyclerView.adapter = adapter
- checkinsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
adapter.setData(informViewModel.selectableCheckinItems)
adapter.itemSelectionListener { selectedItem, selected ->
informViewModel.selectableCheckinItems.find { it.diaryEntry == selectedItem }?.isSelected = selected
@@ -39,7 +38,7 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
if (informViewModel.hasSharedDP3TKeys) {
upload()
} else {
- //TODO: Show "Thanks for nothing" Fragment
+ showFragment(NotThankYouFragment.newInstance(), R.id.inform_fragment_container)
}
}
sendButton.setOnClickListener {
@@ -51,11 +50,7 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
}
private fun upload() {
- performUpload(onSuccess = {
- showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container)
- }, onInvalidCovidCode = {
- showFragment(InformFragment.newInstance(isCovidCodeInvalidCase = true), R.id.inform_fragment_container)
- })
+ performUpload(onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) })
}
override fun setLoadingViewVisible(isVisible: Boolean) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
index 815447238..24106b790 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ThankYouFragment.kt
@@ -20,7 +20,7 @@ import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentThankYouBinding
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.DateUtils
-import ch.admin.bag.dp3t.util.showFragment
+import ch.admin.bag.dp3t.extensions.showFragment
import java.util.*
class ThankYouFragment : Fragment() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
index 286b03d83..ff1295d4c 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/TraceKeyShareBaseFragment.kt
@@ -14,6 +14,7 @@ import ch.admin.bag.dp3t.util.ENExceptionHelper
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import com.google.android.gms.common.api.ApiException
import org.dpppt.android.sdk.DP3T
+import org.dpppt.android.sdk.PendingUploadTask
import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.internal.logger.Logger
@@ -30,8 +31,9 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
abstract fun setLoadingViewVisible(isVisible: Boolean)
protected fun showShareTEKsPopup(onSuccess: () -> Unit, onError: () -> Unit) {
- DP3T.showShareTEKsPopup(requireActivity(), object : ResponseCallback {
- override fun onSuccess(p0: Void?) {
+ DP3T.showShareTEKsPopup(requireActivity(), object : ResponseCallback {
+ override fun onSuccess(pendingUploadTask: PendingUploadTask) {
+ informViewModel.pendingUploadTask = pendingUploadTask
onSuccess()
}
@@ -50,7 +52,7 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
}
}
- protected fun performUpload(onSuccess: () -> Unit, onInvalidCovidCode: () -> Unit) {
+ protected fun performUpload(onSuccess: () -> Unit) {
informViewModel.performUpload().observe(viewLifecycleOwner) {
when (it.status) {
Status.LOADING -> {
@@ -58,7 +60,7 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
}
Status.ERROR -> {
setLoadingViewVisible(false)
- handleUploadException(it, onInvalidCovidCode)
+ handleUploadException(it)
}
Status.SUCCESS -> {
setLoadingViewVisible(false)
@@ -68,12 +70,12 @@ abstract class TraceKeyShareBaseFragment : Fragment() {
}
}
- private fun handleUploadException(error: Resource, onInvalidCovidCode: () -> Unit) {
+ private fun handleUploadException(error: Resource) {
if (error.data == null) return
when (error.exception) {
- is InvalidCodeError -> onInvalidCovidCode()
is ResponseError -> showErrorDialog(error.data, error.exception.statusCode.toString())
is ApiException -> showErrorDialog(error.data, error.exception.statusCode.toString())
+ is InvalidCodeError -> showErrorDialog(error.data)
else -> showErrorDialog(error.data)
}
error.exception?.printStackTrace()
diff --git a/app/src/main/res/drawable/illu_not_thank_you.xml b/app/src/main/res/drawable/illu_not_thank_you.xml
new file mode 100644
index 000000000..22a6cae2c
--- /dev/null
+++ b/app/src/main/res/drawable/illu_not_thank_you.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_not_thank_you.xml b/app/src/main/res/layout/fragment_not_thank_you.xml
new file mode 100644
index 000000000..f2f14ded7
--- /dev/null
+++ b/app/src/main/res/layout/fragment_not_thank_you.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_share_checkins.xml b/app/src/main/res/layout/fragment_share_checkins.xml
index c0548f06d..9f9204a8d 100644
--- a/app/src/main/res/layout/fragment_share_checkins.xml
+++ b/app/src/main/res/layout/fragment_share_checkins.xml
@@ -1,5 +1,6 @@
@@ -57,28 +58,38 @@
android:clipChildren="false"
android:clipToPadding="false"
android:paddingHorizontal="@dimen/spacing_medium_large"
- android:paddingBottom="@dimen/spacing_larger" />
+ android:paddingBottom="@dimen/spacing_larger"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
-
+ android:background="@color/white"
+ android:elevation="@dimen/spacing_small"
+ android:orientation="vertical">
-
+
+
+
+
+
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index abfc01c37..f03163b26 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -1774,4 +1774,15 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index e44a21510..02e9971a3 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1856,4 +1856,20 @@ oder Ihren Anlass näher beschreibt.""Sie könnten bereits ansteckend sein, ohne es zu merken. Beachten Sie die geltenden Hygiene- und Schutz-massnahmen und schützen Sie Ihre Familie, Ihre Freunde und Ihr Umfeld.""Überwachen Sie Ihren Gesundheitszustand und lassen Sie sich beim Auftreten von Symptomen sofort testen.""Auch wenn keine Symptome auftreten, ist ein Corona-Test sinnvoll und empfohlen (ab dem 5. Tag nach dem angezeigten Kontaktdatum)."
+
+
+
+ "Check-In"
+
+
+
+ "Keine Daten gesendet"
+
+
+ "Es wurden keine privaten Schlüssel gesendet."
+ "Zurück"
+ "Keine Daten senden"
+
+
+ "Es wurden keine Check-In-Einträge gesendet. Es wird niemand benachrichtigt."
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cbeb45c1f..1ed406f44 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1765,4 +1765,15 @@ Contacte con la Infoline del coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 265c0f043..2955d7196 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1800,4 +1800,15 @@ Contactez l'infoline coronavirus :"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 14dbac5c1..385000dd6 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1770,4 +1770,15 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index fd5612a4d..03980f0ba 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1808,4 +1808,15 @@ Contatta la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 14c9353d9..953cd7d04 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1775,4 +1775,15 @@ Contacte a linha de informação coronavírus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index e5ccc4cac..9a4397cde 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1770,4 +1770,15 @@ Alura contactai la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 28fda471e..519ff2817 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1775,4 +1775,15 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 28696389f..f770d7e5d 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1770,4 +1770,15 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index cbf131a52..b313f803a 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1750,4 +1750,15 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 2cbec156c..26791cbb6 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1752,4 +1752,15 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2176c162e..edde8e916 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1797,4 +1797,15 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 4e364c8808db0cf87ff0af20ef70de4095ba0f4f Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 21 May 2021 10:22:08 +0200
Subject: [PATCH 075/207] Clean up all Inform Screens
---
.../admin/bag/dp3t/inform/CheckinAdapter.kt | 16 ++
.../main/res/drawable/ill_tracing_beenden.xml | 118 +++++++++----
app/src/main/res/layout/fragment_get_well.xml | 78 +++++----
app/src/main/res/layout/fragment_inform.xml | 2 +-
.../main/res/layout/fragment_inform_intro.xml | 16 +-
.../fragment_inform_really_not_share.xml | 4 +-
.../res/layout/fragment_share_checkins.xml | 7 +-
.../main/res/layout/fragment_thank_you.xml | 155 +++++++++++-------
.../res/layout/fragment_tracing_stopped.xml | 47 ++++--
.../res/layout/item_selectable_checkin.xml | 14 +-
app/src/main/res/values-bs/strings.xml | 8 +-
app/src/main/res/values-de/strings.xml | 24 +--
app/src/main/res/values-es/strings.xml | 8 +-
app/src/main/res/values-fr/strings.xml | 8 +-
app/src/main/res/values-hr/strings.xml | 8 +-
app/src/main/res/values-it/strings.xml | 8 +-
app/src/main/res/values-pt/strings.xml | 8 +-
app/src/main/res/values-rm/strings.xml | 8 +-
app/src/main/res/values-sq/strings.xml | 8 +-
app/src/main/res/values-sr/strings.xml | 8 +-
app/src/main/res/values-ti/strings.xml | 8 +-
app/src/main/res/values-tr/strings.xml | 8 +-
app/src/main/res/values/strings.xml | 8 +-
23 files changed, 389 insertions(+), 188 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
index 02e024a73..561141758 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/CheckinAdapter.kt
@@ -3,6 +3,7 @@ package ch.admin.bag.dp3t.inform
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
+import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.databinding.ItemSelectableCheckinBinding
import ch.admin.bag.dp3t.extensions.getSubtitle
@@ -32,10 +33,25 @@ class CheckinAdapter : RecyclerView.Adapter()
checkinDetail2.text =
StringUtil.getReportDateString(selectableCheckinItem.diaryEntry.departureTime, true, true, root.context)
checkbox.isChecked = selectableCheckinItem.isSelected
+ setStroke(selectableCheckinItem.isSelected)
+
checkbox.setOnCheckedChangeListener { _, isChecked ->
selectableCheckinItem.isSelected = isChecked
+ setStroke(isChecked)
itemSelectionListener?.invoke(selectableCheckinItem.diaryEntry, isChecked)
}
+ root.setOnClickListener { checkbox.isChecked = !checkbox.isChecked }
+ }
+ }
+
+ private fun setStroke(isSelected: Boolean) {
+ binding.apply {
+ if (isSelected) {
+ root.strokeWidth = root.context.resources.getDimensionPixelSize(R.dimen.stroke_width_default)
+ } else {
+ root.strokeWidth = 0
+ }
+ root.requestLayout()
}
}
}
diff --git a/app/src/main/res/drawable/ill_tracing_beenden.xml b/app/src/main/res/drawable/ill_tracing_beenden.xml
index 156850241..3b1871b91 100644
--- a/app/src/main/res/drawable/ill_tracing_beenden.xml
+++ b/app/src/main/res/drawable/ill_tracing_beenden.xml
@@ -1,26 +1,10 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_get_well.xml b/app/src/main/res/layout/fragment_get_well.xml
index b628aee5f..27a9c66e6 100644
--- a/app/src/main/res/layout/fragment_get_well.xml
+++ b/app/src/main/res/layout/fragment_get_well.xml
@@ -7,41 +7,58 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
+ android:orientation="vertical">
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:fillViewport="true">
-
+ android:orientation="vertical">
-
+
-
+
+
+
+
+
+
+
+
-
-
\ No newline at end of file
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_inform.xml b/app/src/main/res/layout/fragment_inform.xml
index 618ea4086..9022b1244 100644
--- a/app/src/main/res/layout/fragment_inform.xml
+++ b/app/src/main/res/layout/fragment_inform.xml
@@ -26,7 +26,7 @@
style="@style/NextStep.Button.Transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="end"
+ android:layout_gravity="start"
android:text="@string/cancel" />
+
+
+ android:layout_marginTop="@dimen/spacing_large"
+ android:layout_marginBottom="@dimen/spacing_huge">
diff --git a/app/src/main/res/layout/fragment_inform_really_not_share.xml b/app/src/main/res/layout/fragment_inform_really_not_share.xml
index 8ae2804a1..c9391e928 100644
--- a/app/src/main/res/layout/fragment_inform_really_not_share.xml
+++ b/app/src/main/res/layout/fragment_inform_really_not_share.xml
@@ -40,7 +40,7 @@
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/spacing_huge"
android:layout_marginBottom="@dimen/spacing_medium_large"
- android:text="@string/inform_try_again_button_title" />
+ android:text="@string/share_button_title" />
diff --git a/app/src/main/res/layout/fragment_share_checkins.xml b/app/src/main/res/layout/fragment_share_checkins.xml
index 9f9204a8d..dba5940fe 100644
--- a/app/src/main/res/layout/fragment_share_checkins.xml
+++ b/app/src/main/res/layout/fragment_share_checkins.xml
@@ -46,7 +46,8 @@
+ android:layout_height="wrap_content"
+ android:buttonTint="@color/purple_main" />
@@ -75,7 +76,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="@dimen/spacing_medium_large"
- android:text="@string/inform_send_button_title" />
+ android:text="@string/inform_share_checkins_send_button_title" />
diff --git a/app/src/main/res/layout/fragment_thank_you.xml b/app/src/main/res/layout/fragment_thank_you.xml
index aea5e8bd1..f4c671a7f 100644
--- a/app/src/main/res/layout/fragment_thank_you.xml
+++ b/app/src/main/res/layout/fragment_thank_you.xml
@@ -7,74 +7,102 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
+ android:orientation="vertical">
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/white"
+ android:fillViewport="true">
-
+ android:orientation="vertical">
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tracing_stopped.xml b/app/src/main/res/layout/fragment_tracing_stopped.xml
index 0457f8abb..dd6f39bd3 100644
--- a/app/src/main/res/layout/fragment_tracing_stopped.xml
+++ b/app/src/main/res/layout/fragment_tracing_stopped.xml
@@ -9,8 +9,9 @@
-->
+ android:fillViewport="true">
+
+
+
+
-
+
+
+ android:background="@color/white"
+ android:elevation="@dimen/spacing_small">
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_selectable_checkin.xml b/app/src/main/res/layout/item_selectable_checkin.xml
index df4f26910..6606806de 100644
--- a/app/src/main/res/layout/item_selectable_checkin.xml
+++ b/app/src/main/res/layout/item_selectable_checkin.xml
@@ -1,11 +1,15 @@
-
+ app:cardBackgroundColor="@color/white"
+ app:cardForegroundColor="@color/transparent"
+ android:layout_marginTop="@dimen/spacing_medium"
+ app:strokeColor="@color/purple_main"
+ app:strokeWidth="2dp">
+ app:layout_constraintTop_toTopOf="parent"
+ tools:checked="true" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index f03163b26..edcb81e6a 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -1630,7 +1630,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1683,7 +1683,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1785,4 +1785,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 02e9971a3..d492b6f05 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -260,7 +260,7 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Befolgen Sie die erhaltenen Anweisungen zur Isolation"
- "Tracing deaktiviert"
+ "Tracing und Check-ins deaktiviert""Es werden keine Begegnungen mehr gespeichert.""Kontaktieren Sie die""Befolgen Sie die"
@@ -658,7 +658,7 @@ Damit erfahren andere SwissCovid App Nutzer, dass die Möglichkeit einer Ansteck
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Wichtiger Hinweis"
+ "Information zur Eingabe""Obwohl keine Daten zu Ihrer Person gesendet werden, kann es sein, dass sich jemand anhand des Datums an die Begegnung erinnern kann."
@@ -1298,9 +1298,9 @@ Damit auch Reisende aus dem Ausland gewarnt werden können, geschieht das unabh
"Beenden"
- "Es werden keine Begegnungen mehr gespeichert.
+ "Es werden keine neuen Begegnungen mehr gespeichert und neue Check-ins sind derzeit nicht möglich.
-Das Tracing kann nach der Isolation wieder aktiviert werden."
+Beide Funktionen können nach der Isolation wieder aktiviert werden.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1435,7 +1435,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"{ONSET_DATE} – heute"
- "Damit helfen Sie mit, Infektionsketten zu stoppen."
+ "Auf diese Weise helfen Sie, Infektionsketten zu durchbrechen.""Die privaten Schlüssel Ihrer App wurden für den folgenden Zeitraum gesendet:"
@@ -1692,18 +1692,18 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Scannen Sie einen SwissCovid-QR-Code, um sich vor Ort einzuchecken."
- "Check-Ins teilen"
+ "Check-in-Einträge teilen"
- "Sie können Events teilen um andere zu warnen, welche mit Ihnen am selben Event waren."
+ "Wenn Sie Ihre Check-in-Einträge teilen, kann die App Personen benachrichtigen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben.""Alle wählen""Nicht senden""Erneut versuchen""Nicht teilen"
- "Sind Sie sicher?"
- "[TBT] Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified."
+ "Wirklich nicht teilen?"
+ "Das Teilen Ihrer privaten Schlüssel hilft «SwissCovid», andere potenziell infizierte Personen zu benachrichtigen und die weitere Ausbreitung des Virus zu verhindern. Wenn Sie Ihre privaten Schlüssel nicht teilen, werden keine potenziell infizierten Personen benachrichtigt.""Achtung"
@@ -1749,7 +1749,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Ihre gewählten Zeiten überschneiden sich mit anderen Check-Ins aus ihrem Tagebuch. Bitte überprüfen Sie nochmals Ihre Angabe."
- "Ihre ausgewählten Check-Ins wurden gesendet. Andere Leute, welche zur gleichen Zeit an den selben Events eingecheckt waren werden benachrichtigt."
+ "Ihre ausgewählten Check-In-Einträge wurden gesendet. Alle Personen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben, werden benachrichtigt.""Are you sure?"
@@ -1872,4 +1872,8 @@ oder Ihren Anlass näher beschreibt."
"Es wurden keine Check-In-Einträge gesendet. Es wird niemand benachrichtigt."
+
+
+ "Auswahl teilen"
+ "Nichts teilen"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 1ed406f44..b043dfcb0 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1621,7 +1621,7 @@ Contacte con la Infoline del coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1674,7 +1674,7 @@ Contacte con la Infoline del coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1776,4 +1776,8 @@ Contacte con la Infoline del coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 2955d7196..ab506dc42 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1656,7 +1656,7 @@ Contactez l'infoline coronavirus :"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1709,7 +1709,7 @@ Contactez l'infoline coronavirus :"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1811,4 +1811,8 @@ Contactez l'infoline coronavirus :"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 385000dd6..16d803b2b 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1626,7 +1626,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1679,7 +1679,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1781,4 +1781,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 03980f0ba..5b14459a4 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1664,7 +1664,7 @@ Contatta la Infoline coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1717,7 +1717,7 @@ Contatta la Infoline coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1819,4 +1819,8 @@ Contatta la Infoline coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 953cd7d04..0f7f59809 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1631,7 +1631,7 @@ Contacte a linha de informação coronavírus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1684,7 +1684,7 @@ Contacte a linha de informação coronavírus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1786,4 +1786,8 @@ Contacte a linha de informação coronavírus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 9a4397cde..622d10270 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1626,7 +1626,7 @@ Alura contactai la Infoline coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1679,7 +1679,7 @@ Alura contactai la Infoline coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1781,4 +1781,8 @@ Alura contactai la Infoline coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 519ff2817..22641f8e9 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1631,7 +1631,7 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1684,7 +1684,7 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1786,4 +1786,8 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index f770d7e5d..48a0dbf3f 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1626,7 +1626,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1679,7 +1679,7 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1781,4 +1781,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index b313f803a..cfc557ac7 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1606,7 +1606,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1659,7 +1659,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1761,4 +1761,8 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 26791cbb6..938b5d160 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1608,7 +1608,7 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1661,7 +1661,7 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1763,4 +1763,8 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index edde8e916..24c33d04f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1653,7 +1653,7 @@ If so, contact the Coronavirus Infoline:"
- "You can choose to share your check-ins to warn others who were with you at the same events."
+
@@ -1706,7 +1706,7 @@ If so, contact the Coronavirus Infoline:"
- "Your selected check-in entries were sent. Other people who checked into the same events and during the same period of time will be notified."
+ "Are you sure?"
@@ -1808,4 +1808,8 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
\ No newline at end of file
From 05f70d2612d841b5e1a3dc1b9f48588e15e16f35 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 14:19:11 +0200
Subject: [PATCH 076/207] Add OnsetDate Request
---
.../admin/bag/dp3t/inform/InformFragment.kt | 21 +++++++---
.../admin/bag/dp3t/inform/InformViewModel.kt | 42 ++++++++++++++++++-
.../bag/dp3t/inform/ReallyNotShareFragment.kt | 2 +-
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 11 ++---
.../bag/dp3t/networking/AuthCodeRepository.kt | 1 +
.../bag/dp3t/networking/AuthCodeService.kt | 5 +++
.../dp3t/networking/models/AuthCodeModels.kt | 12 ++++++
.../AuthenticationCodeRequestModel.java | 26 ------------
.../AuthenticationCodeResponseModelV2.kt | 6 ---
9 files changed, 81 insertions(+), 45 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeRequestModel.java
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
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 2836a7a6f..7f453fe51 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
@@ -17,6 +17,7 @@ import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentInformBinding
import ch.admin.bag.dp3t.extensions.showFragment
+import ch.admin.bag.dp3t.inform.models.Status
import ch.admin.bag.dp3t.inform.views.ChainedEditText
import ch.admin.bag.dp3t.inform.views.ChainedEditText.ChainedEditTextListener
import ch.admin.bag.dp3t.util.PhoneUtil
@@ -64,29 +65,39 @@ class InformFragment : TraceKeyShareBaseFragment() {
}
}
- sendButton.setOnClickListener { performContinueAction() }
+ sendButton.setOnClickListener { onContinueClicked() }
cancelButton.setOnClickListener { requireActivity().finish() }
informInvalidCodeError.setOnClickListener { PhoneUtil.callAppHotline(it.context) }
}.root
}
- private fun performContinueAction() {
+ private fun onContinueClicked() {
binding.sendButton.isEnabled = false
setInvalidCovidcodeErrorVisible(false)
informViewModel.covidCode = binding.covidcodeInput.text
askUserToEnableTracingIfNecessary { tracingEnabled ->
if (tracingEnabled) {
- //TODO: Do onsetDate Request here
- showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
+ loadOnsetDate()
} else {
binding.sendButton.isEnabled = true
}
}
}
+ private fun loadOnsetDate() {
+ informViewModel.loadOnsetDate().observe(viewLifecycleOwner) {
+ setLoadingViewVisible(it.status == Status.LOADING)
+ if (it.status == Status.SUCCESS) {
+ showShareTEKsPopup(onSuccess = ::onUserGrantedTEKSharing, onError = ::onUserDidNotGrantTEKSharing)
+ } else if (it.status == Status.ERROR) {
+ setInvalidCovidcodeErrorVisible(true)
+ }
+ }
+ }
+
private fun onUserGrantedTEKSharing() {
informViewModel.hasSharedDP3TKeys = true
- if (informViewModel.selectableCheckinItems.isEmpty()) {
+ if (informViewModel.getSelectableCheckinItems().isEmpty()) {
performUpload()
} else {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 1479b188a..7a78a0572 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -26,6 +26,7 @@ import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.models.DayDate
import org.dpppt.android.sdk.models.ExposeeAuthMethodAuthorization
import retrofit2.HttpException
+import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.CancellationException
import java.util.concurrent.TimeUnit
@@ -40,6 +41,8 @@ private const val KEY_COVIDCODE = "KEY_COVIDCODE"
private const val KEY_HAS_SHARED_DP3T_KEYS = "KEY_HAS_SHARED_DP3T_KEYS"
private const val KEY_HAS_SHARED_CHECKINS = "KEY_HAS_SHARED_CHECKINS"
private const val KEY_PENDING_UPLOAD_TASK = "KEY_PENDING_UPLOAD_TASK"
+private const val KEY_SELECTED_DIARY_ENTRIES = "KEY_SELECTED_DIARY_ENTRIES"
+private const val KEY_ONSET_DATE = "KEY_ONSET_DATE"
private const val USER_UPLOAD_SIZE = 1000
class InformViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) {
@@ -50,7 +53,9 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private val secureStorage = SecureStorage.getInstance(application)
private val random = Random()
- var selectableCheckinItems = diaryStorage.entries.map { SelectableCheckinItem(it, isSelected = false) }
+ private var selectedDiaryEntryIds: LongArray
+ get() = state.get(KEY_SELECTED_DIARY_ENTRIES) ?: longArrayOf()
+ set(value) = state.set(KEY_SELECTED_DIARY_ENTRIES, value)
var covidCode: String
get() = state.get(KEY_COVIDCODE) ?: getLastCovidcode()
@@ -68,6 +73,19 @@ class InformViewModel(application: Application, private val state: SavedStateHan
get() = state.get(KEY_PENDING_UPLOAD_TASK)
set(value) = state.set(KEY_PENDING_UPLOAD_TASK, value)
+ private var onsetDate: Long?
+ get() = state.get(KEY_ONSET_DATE)
+ set(value) = state.set(KEY_ONSET_DATE, value)
+
+ fun loadOnsetDate() = liveData(Dispatchers.IO) {
+ emit(Resource.loading(data = null))
+ try {
+ emit(Resource.success(loadOnsetDate(covidCode)))
+ } catch (exception: Throwable) {
+ emit(Resource.error(data = null, exception = exception))
+ }
+ }
+
fun performUpload() = liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
@@ -109,6 +127,20 @@ class InformViewModel(application: Application, private val state: SavedStateHan
emit(Resource.success(data = null))
}
+ fun getSelectableCheckinItems(): List {
+ return diaryStorage.entries.filter {
+ it.departureTime >= onsetDate ?: 0
+ }.map {
+ SelectableCheckinItem(it, isSelected = selectedDiaryEntryIds.contains(it.id))
+ }
+ }
+
+ fun setDiaryItemSelected(itemId: Long, isSelected: Boolean) {
+ selectedDiaryEntryIds = selectedDiaryEntryIds.toMutableList().apply {
+ if (isSelected) add(itemId) else remove(itemId)
+ }.distinct().toLongArray()
+ }
+
private fun getLastCovidcode(): String {
val lastCovidcode = secureStorage.lastInformCode
@@ -155,6 +187,12 @@ class InformViewModel(application: Application, private val state: SavedStateHan
error?.let { throw(it) }
}
+ private suspend fun loadOnsetDate(covidcode: String) {
+ val onsetResponse = authCodeRepository.getOnsetDate(AuthenticationCodeRequestModel(covidcode, 0))
+ if (onsetResponse.onset == null) throw InvalidCodeError()
+ onsetDate = SimpleDateFormat("yyyy-MM-dd").parse(onsetResponse.onset)?.time
+ }
+
private suspend fun loadAccessTokens(covidcode: String) {
val lastTimestamp = secureStorage.lastInformRequestTime
val accessToken = secureStorage.lastDP3TInformToken
@@ -169,7 +207,7 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private fun getUserUploadPayload(): UserUploadPayload {
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
- selectableCheckinItems.filter {
+ getSelectableCheckinItems().filter {
it.isSelected
}.map {
CrowdNotifier.generateUserUploadInfo(it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
index 4499d4c0f..fb37d464a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ReallyNotShareFragment.kt
@@ -40,7 +40,7 @@ class ReallyNotShareFragment : TraceKeyShareBaseFragment() {
private fun onUserGrantedTEKSharing() {
informViewModel.hasSharedDP3TKeys = true
- if (informViewModel.selectableCheckinItems.isEmpty()) {
+ if (informViewModel.getSelectableCheckinItems().isEmpty()) {
performUpload(onSuccess = { showFragment(ThankYouFragment.newInstance(), R.id.inform_fragment_container) })
} else {
showFragment(ShareCheckinsFragment.newInstance(), R.id.inform_fragment_container)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index df9d8cf3b..3a870f0fa 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -6,7 +6,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
-import androidx.recyclerview.widget.LinearLayoutManager
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentShareCheckinsBinding
import ch.admin.bag.dp3t.extensions.showFragment
@@ -25,13 +24,15 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
(requireActivity() as InformActivity).allowBackButton(false)
val adapter = CheckinAdapter()
checkinsRecyclerView.adapter = adapter
- adapter.setData(informViewModel.selectableCheckinItems)
+ adapter.setData(informViewModel.getSelectableCheckinItems())
adapter.itemSelectionListener { selectedItem, selected ->
- informViewModel.selectableCheckinItems.find { it.diaryEntry == selectedItem }?.isSelected = selected
+ informViewModel.setDiaryItemSelected(selectedItem.id, selected)
}
selectAllCheckbox.setOnCheckedChangeListener { _, isChecked ->
- informViewModel.selectableCheckinItems.forEach { it.isSelected = isChecked }
- adapter.setData(informViewModel.selectableCheckinItems)
+ informViewModel.getSelectableCheckinItems().forEach {
+ informViewModel.setDiaryItemSelected(it.diaryEntry.id, isChecked)
+ }
+ adapter.setData(informViewModel.getSelectableCheckinItems())
}
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
index ce4156d39..4b1de98bd 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
@@ -80,4 +80,5 @@ class AuthCodeRepository(context: Context) {
return@withContext response.body() ?: throw ResponseError(response.raw())
}
+ suspend fun getOnsetDate(authCode: AuthenticationCodeRequestModel) = authCodeService.getOnsetDate(authCode)
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
index 24308fea0..2d7c69330 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
@@ -12,6 +12,7 @@ package ch.admin.bag.dp3t.networking
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModel
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModelV2
+import ch.admin.bag.dp3t.networking.models.OnsetResponse
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
@@ -26,4 +27,8 @@ interface AuthCodeService {
@POST("v2/onset")
suspend fun getAccessTokenV2(@Body code: AuthenticationCodeRequestModel): Response
+ @Headers("accept: */*", "content-type: application/json")
+ @POST("v2/onset/date")
+ suspend fun getOnsetDate(@Body code: AuthenticationCodeRequestModel): OnsetResponse
+
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
new file mode 100644
index 000000000..26a9956f0
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
@@ -0,0 +1,12 @@
+package ch.admin.bag.dp3t.networking.models
+
+import androidx.annotation.Keep
+
+class AuthenticationCodeRequestModel(@field:Keep private val authorizationCode: String, @field:Keep private val fake: Int)
+
+data class AuthenticationCodeResponseModelV2(
+ val checkInAccessToken: AuthenticationCodeResponseModel,
+ val dp3TAccessToken: AuthenticationCodeResponseModel
+)
+
+data class OnsetResponse(val onset: String?)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeRequestModel.java b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeRequestModel.java
deleted file mode 100644
index 4435beba4..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeRequestModel.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.networking.models;
-
-import androidx.annotation.Keep;
-
-public class AuthenticationCodeRequestModel {
-
- @Keep
- private String authorizationCode;
- @Keep
- private int fake;
-
- public AuthenticationCodeRequestModel(String authorizationCode, int fake) {
- this.authorizationCode = authorizationCode;
- this.fake = fake;
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
deleted file mode 100644
index 331276821..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModelV2.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package ch.admin.bag.dp3t.networking.models
-
-data class AuthenticationCodeResponseModelV2(
- val checkInAccessToken: AuthenticationCodeResponseModel,
- val dp3TAccessToken: AuthenticationCodeResponseModel
-)
\ No newline at end of file
From 2cace1879bcb89a7082e33fd4a6a9e9c3172e42f Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 14:46:00 +0200
Subject: [PATCH 077/207] Implemented Fake request Logic for Inform Flow
---
.../admin/bag/dp3t/inform/InformViewModel.kt | 72 +++++---
.../bag/dp3t/inform/NotThankYouFragment.kt | 21 ++-
.../res/layout/fragment_not_thank_you.xml | 172 ++++++++++--------
3 files changed, 156 insertions(+), 109 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index 7a78a0572..dcdbe3e8a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -21,6 +21,7 @@ import com.google.android.gms.common.api.ApiException
import com.google.protobuf.ByteString
import kotlinx.coroutines.Dispatchers
import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.dpppt.android.sdk.DP3TKotlin
import org.dpppt.android.sdk.PendingUploadTask
import org.dpppt.android.sdk.backend.ResponseCallback
import org.dpppt.android.sdk.models.DayDate
@@ -98,32 +99,32 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
return@liveData
}
- if (hasSharedDP3TKeys) {
- try {
+ try {
+ if (hasSharedDP3TKeys) {
uploadTEKs()
- } catch (exception: Throwable) {
- when (exception) {
- is ResponseError -> emit(Resource.error(InformRequestError.RED_STATUS_ERROR, exception))
- is CancellationException -> emit(Resource.error(InformRequestError.RED_USER_CANCELLED_SHARE, exception))
- is ApiException -> emit(Resource.error(InformRequestError.RED_EXPOSURE_API_ERROR, exception))
- else -> emit(Resource.error(InformRequestError.RED_MISC_NETWORK_ERROR, exception))
- }
- return@liveData
+ } else {
+ performFakeTEKUpload()
}
+ } catch (exception: Throwable) {
+ when (exception) {
+ is ResponseError -> emit(Resource.error(InformRequestError.RED_STATUS_ERROR, exception))
+ is CancellationException -> emit(Resource.error(InformRequestError.RED_USER_CANCELLED_SHARE, exception))
+ is ApiException -> emit(Resource.error(InformRequestError.RED_EXPOSURE_API_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.RED_MISC_NETWORK_ERROR, exception))
+ }
+ return@liveData
}
- if (hasSharedCheckins) {
- try {
- val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
- userUploadRepository.userUpload(getUserUploadPayload(), authorizationHeader)
-
- } catch (exception: Throwable) {
- when (exception) {
- is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
- else -> emit(Resource.error(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, exception))
- }
- return@liveData
+
+ try {
+ performCheckinsUpload(isFake = !hasSharedCheckins)
+ } catch (exception: Throwable) {
+ when (exception) {
+ is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
+ else -> emit(Resource.error(InformRequestError.USER_UPLOAD_UNKONWN_ERROR, exception))
}
+ return@liveData
}
+
emit(Resource.success(data = null))
}
@@ -151,6 +152,11 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
}
+ private suspend fun performFakeTEKUpload() {
+ val authorizationHeader = ExposeeAuthMethodAuthorization(getAuthorizationHeader(secureStorage.lastDP3TInformToken))
+ DP3TKotlin.sendFakeInfectedRequest(getApplication(), authorizationHeader)
+ }
+
private suspend fun uploadTEKs() {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastDP3TInformToken)
val onsetDate = JwtUtil.getOnsetDate(secureStorage.lastDP3TInformToken)
@@ -187,6 +193,11 @@ class InformViewModel(application: Application, private val state: SavedStateHan
error?.let { throw(it) }
}
+ private suspend fun performCheckinsUpload(isFake: Boolean) {
+ val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
+ userUploadRepository.userUpload(getUserUploadPayload(isFake), authorizationHeader)
+ }
+
private suspend fun loadOnsetDate(covidcode: String) {
val onsetResponse = authCodeRepository.getOnsetDate(AuthenticationCodeRequestModel(covidcode, 0))
if (onsetResponse.onset == null) throw InvalidCodeError()
@@ -205,16 +216,19 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
}
- private fun getUserUploadPayload(): UserUploadPayload {
+ private fun getUserUploadPayload(isFake: Boolean): UserUploadPayload {
val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
- getSelectableCheckinItems().filter {
- it.isSelected
- }.map {
- CrowdNotifier.generateUserUploadInfo(it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime)
- }.flatten().forEach {
- userUploadPayloadBuilder.addVenueInfos(it.toUploadVenueInfo())
+ if (!isFake) {
+ getSelectableCheckinItems().filter {
+ it.isSelected
+ }.map {
+ CrowdNotifier.generateUserUploadInfo(
+ it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime
+ )
+ }.flatten().forEach {
+ userUploadPayloadBuilder.addVenueInfos(it.toUploadVenueInfo())
+ }
}
-
for (i in userUploadPayloadBuilder.venueInfosCount until USER_UPLOAD_SIZE) {
userUploadPayloadBuilder.addVenueInfos(getRandomFakeVenueInfo())
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
index 6633ba0e5..171ab7617 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/NotThankYouFragment.kt
@@ -5,23 +5,36 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.Fragment
+import androidx.core.view.isVisible
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.databinding.FragmentNotThankYouBinding
import ch.admin.bag.dp3t.extensions.showFragment
-class NotThankYouFragment : Fragment() {
+class NotThankYouFragment : TraceKeyShareBaseFragment() {
companion object {
fun newInstance() = NotThankYouFragment()
}
+ private lateinit var binding: FragmentNotThankYouBinding
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- return FragmentNotThankYouBinding.inflate(inflater).apply {
+ binding = FragmentNotThankYouBinding.inflate(inflater)
+ return binding.apply {
(requireActivity() as InformActivity).allowBackButton(false)
- dontSendButton.setOnClickListener { requireActivity().finish() }
+ dontSendButton.setOnClickListener {
+ performUpload(onSuccess = { requireActivity().finish() })
+ }
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
backButton.setOnClickListener { showFragment(ReallyNotShareFragment.newInstance(), R.id.inform_fragment_container) }
}.root
}
+
+ override fun setLoadingViewVisible(isVisible: Boolean) {
+ binding.apply {
+ loadingView.isVisible = isVisible
+ dontSendButton.isEnabled = !isVisible
+ backButton.isEnabled = !isVisible
+ }
+ }
}
\ No newline at end of file
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 f2f14ded7..aef45eb70 100644
--- a/app/src/main/res/layout/fragment_not_thank_you.xml
+++ b/app/src/main/res/layout/fragment_not_thank_you.xml
@@ -1,100 +1,120 @@
-
+ android:layout_height="match_parent">
-
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:orientation="vertical">
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:fillViewport="true">
-
-
-
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+ android:text="@string/not_thank_you_screen_back_button" />
-
-
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="@dimen/spacing_medium"
+ android:layout_marginBottom="@dimen/spacing_medium_large"
+ android:background="@drawable/ripple_button"
+ android:padding="@dimen/spacing_small"
+ android:text="@string/not_thank_you_screen_dont_send_button"
+ android:visibility="visible" />
-
+
-
-
-
+ android:layout_height="match_parent"
+ android:background="@color/transparent_milk"
+ android:visibility="gone">
-
+ android:layout_gravity="center" />
-
+
-
\ No newline at end of file
+
From ce3e71ce63ed6bdf3405fe43d0177f15de16fad4 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 15:25:20 +0200
Subject: [PATCH 078/207] Adjusted FakeWorker Logic
---
.../networking/UserUploadRepository.kt | 38 ++++++++++++-
.../admin/bag/dp3t/inform/InformViewModel.kt | 54 +++++--------------
.../bag/dp3t/networking/AuthCodeRepository.kt | 11 ----
.../bag/dp3t/networking/AuthCodeService.kt | 4 --
.../admin/bag/dp3t/networking/FakeWorker.kt | 20 +++++--
.../dp3t/networking/models/AuthCodeModels.kt | 3 ++
.../AuthenticationCodeResponseModel.java | 20 -------
7 files changed, 69 insertions(+), 81 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModel.java
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
index 373947cdc..caf8bb6d9 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/UserUploadRepository.kt
@@ -1,16 +1,23 @@
package ch.admin.bag.dp3t.checkin.networking
import ch.admin.bag.dp3t.BuildConfig
+import ch.admin.bag.dp3t.checkin.models.UploadVenueInfo
import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
+import com.google.protobuf.ByteString
import okhttp3.OkHttpClient
import org.dpppt.android.sdk.DP3T
import org.dpppt.android.sdk.backend.UserAgentInterceptor
import retrofit2.Retrofit
import retrofit2.converter.protobuf.ProtoConverterFactory
+import java.util.*
+
+private const val USER_UPLOAD_SIZE = 1000
+private const val USER_UPLOAD_VERSION = 3
class UserUploadRepository {
private var userUploadService: UserUploadService
+ private val random = Random()
init {
val okHttpBuilder = OkHttpClient.Builder()
@@ -25,8 +32,35 @@ class UserUploadRepository {
userUploadService = retrofit.create(UserUploadService::class.java)
}
- suspend fun userUpload(userUploadPayload: UserUploadPayload, authorizationHeader: String) =
- userUploadService.userUpload(userUploadPayload, authorizationHeader)
+ suspend fun userUpload(uploadVenueInfos: List, authorizationHeader: String) =
+ userUploadService.userUpload(getUserUploadPayload(uploadVenueInfos), authorizationHeader)
+
+ suspend fun fakeUserUpload(authorizationHeader: String) =
+ userUploadService.userUpload(getUserUploadPayload(listOf()), authorizationHeader)
+ private fun getUserUploadPayload(uploadVenueInfos: List): UserUploadPayload {
+ val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
+ userUploadPayloadBuilder.addAllVenueInfos(uploadVenueInfos)
+ for (i in userUploadPayloadBuilder.venueInfosCount until USER_UPLOAD_SIZE) {
+ userUploadPayloadBuilder.addVenueInfos(getRandomFakeVenueInfo())
+ }
+ return userUploadPayloadBuilder.build()
+ }
+ private fun getRandomFakeVenueInfo(): UploadVenueInfo {
+ return UploadVenueInfo.newBuilder()
+ .setPreId(ByteString.copyFrom(getRandomByteArray(32)))
+ .setTimeKey(ByteString.copyFrom(getRandomByteArray(32)))
+ .setNotificationKey(ByteString.copyFrom(getRandomByteArray(32)))
+ .setIntervalStartMs(random.nextLong())
+ .setIntervalEndMs(random.nextLong())
+ .setFake(true)
+ .build()
+ }
+
+ private fun getRandomByteArray(size: Int): ByteArray {
+ return ByteArray(size).apply {
+ random.nextBytes(this)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index dcdbe3e8a..490d48d79 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -5,7 +5,6 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.liveData
import ch.admin.bag.dp3t.checkin.models.UploadVenueInfo
-import ch.admin.bag.dp3t.checkin.models.UserUploadPayload
import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.inform.models.Resource
@@ -18,7 +17,6 @@ import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.JwtUtil
import ch.admin.bag.dp3t.util.toUploadVenueInfo
import com.google.android.gms.common.api.ApiException
-import com.google.protobuf.ByteString
import kotlinx.coroutines.Dispatchers
import org.crowdnotifier.android.sdk.CrowdNotifier
import org.dpppt.android.sdk.DP3TKotlin
@@ -34,7 +32,6 @@ import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
-private const val USER_UPLOAD_VERSION = 3
private const val TIMEOUT_VALID_CODE = 1000L * 60 * 5
private const val MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L
private const val ISOLATION_DURATION_DAYS = 14L
@@ -44,7 +41,6 @@ private const val KEY_HAS_SHARED_CHECKINS = "KEY_HAS_SHARED_CHECKINS"
private const val KEY_PENDING_UPLOAD_TASK = "KEY_PENDING_UPLOAD_TASK"
private const val KEY_SELECTED_DIARY_ENTRIES = "KEY_SELECTED_DIARY_ENTRIES"
private const val KEY_ONSET_DATE = "KEY_ONSET_DATE"
-private const val USER_UPLOAD_SIZE = 1000
class InformViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) {
@@ -52,7 +48,6 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private val userUploadRepository = UserUploadRepository()
private val diaryStorage = DiaryStorage.getInstance(application)
private val secureStorage = SecureStorage.getInstance(application)
- private val random = Random()
private var selectedDiaryEntryIds: LongArray
get() = state.get(KEY_SELECTED_DIARY_ENTRIES) ?: longArrayOf()
@@ -195,7 +190,11 @@ class InformViewModel(application: Application, private val state: SavedStateHan
private suspend fun performCheckinsUpload(isFake: Boolean) {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
- userUploadRepository.userUpload(getUserUploadPayload(isFake), authorizationHeader)
+ if (isFake) {
+ userUploadRepository.fakeUserUpload(authorizationHeader)
+ } else {
+ userUploadRepository.userUpload(getUploadVenueInfos(), authorizationHeader)
+ }
}
private suspend fun loadOnsetDate(covidcode: String) {
@@ -216,40 +215,15 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
}
- private fun getUserUploadPayload(isFake: Boolean): UserUploadPayload {
- val userUploadPayloadBuilder = UserUploadPayload.newBuilder().setVersion(USER_UPLOAD_VERSION)
- if (!isFake) {
- getSelectableCheckinItems().filter {
- it.isSelected
- }.map {
- CrowdNotifier.generateUserUploadInfo(
- it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime
- )
- }.flatten().forEach {
- userUploadPayloadBuilder.addVenueInfos(it.toUploadVenueInfo())
- }
- }
- for (i in userUploadPayloadBuilder.venueInfosCount until USER_UPLOAD_SIZE) {
- userUploadPayloadBuilder.addVenueInfos(getRandomFakeVenueInfo())
- }
- return userUploadPayloadBuilder.build()
- }
-
- private fun getRandomFakeVenueInfo(): UploadVenueInfo {
- return UploadVenueInfo.newBuilder()
- .setPreId(ByteString.copyFrom(getRandomByteArray(32)))
- .setTimeKey(ByteString.copyFrom(getRandomByteArray(32)))
- .setNotificationKey(ByteString.copyFrom(getRandomByteArray(32)))
- .setIntervalStartMs(random.nextLong())
- .setIntervalEndMs(random.nextLong())
- .setFake(true)
- .build()
-
- }
-
- private fun getRandomByteArray(size: Int): ByteArray {
- return ByteArray(size).apply {
- random.nextBytes(this)
+ private fun getUploadVenueInfos(): List {
+ return getSelectableCheckinItems().filter {
+ it.isSelected
+ }.map {
+ CrowdNotifier.generateUserUploadInfo(
+ it.diaryEntry.venueInfo, it.diaryEntry.arrivalTime, it.diaryEntry.departureTime
+ )
+ }.flatten().map {
+ it.toUploadVenueInfo()
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
index 4b1de98bd..93d046613 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeRepository.kt
@@ -14,7 +14,6 @@ import ch.admin.bag.dp3t.BuildConfig
import ch.admin.bag.dp3t.networking.errors.InvalidCodeError
import ch.admin.bag.dp3t.networking.errors.ResponseError
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
-import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModel
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeResponseModelV2
import kotlinx.coroutines.*
import okhttp3.Cache
@@ -24,7 +23,6 @@ import org.dpppt.android.sdk.DP3T
import org.dpppt.android.sdk.backend.UserAgentInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
-import java.io.IOException
class AuthCodeRepository(context: Context) {
@@ -58,15 +56,6 @@ class AuthCodeRepository(context: Context) {
authCodeService = retrofit.create(AuthCodeService::class.java)
}
-
- @Throws(IOException::class, ResponseError::class)
- suspend fun getAccessTokenSyncV1(authenticationCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModel =
- withContext(Dispatchers.IO) {
- val response = authCodeService.getAccessTokenV1(authenticationCode)
- if (!response.isSuccessful) throw ResponseError(response.raw())
- return@withContext response.body() ?: throw ResponseError(response.raw())
- }
-
suspend fun getAccessToken(authCode: AuthenticationCodeRequestModel): AuthenticationCodeResponseModelV2 =
withContext(Dispatchers.IO) {
val response = authCodeService.getAccessTokenV2(authCode)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
index 2d7c69330..cc1322c97 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/AuthCodeService.kt
@@ -19,10 +19,6 @@ import retrofit2.http.Headers
import retrofit2.http.POST
interface AuthCodeService {
- @Headers("accept: */*", "content-type: application/json")
- @POST("v1/onset")
- suspend fun getAccessTokenV1(@Body code: AuthenticationCodeRequestModel): Response
-
@Headers("accept: */*", "content-type: application/json")
@POST("v2/onset")
suspend fun getAccessTokenV2(@Body code: AuthenticationCodeRequestModel): Response
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
index f8321b80a..da5a80c3b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/FakeWorker.kt
@@ -12,6 +12,7 @@ package ch.admin.bag.dp3t.networking
import android.content.Context
import androidx.work.*
import ch.admin.bag.dp3t.BuildConfig
+import ch.admin.bag.dp3t.checkin.networking.UserUploadRepository
import ch.admin.bag.dp3t.networking.models.AuthenticationCodeRequestModel
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.ExponentialDistribution
@@ -145,11 +146,22 @@ class FakeWorker(context: Context, workerParams: WorkerParameters) : CoroutineWo
private suspend fun executeFakeRequest(context: Context): Boolean {
return try {
val authCodeRepository = AuthCodeRepository(context)
- val accessTokenResponse = authCodeRepository.getAccessTokenSyncV1(AuthenticationCodeRequestModel(FAKE_AUTH_CODE, 1))
- val accessToken = accessTokenResponse.accessToken
- DP3TKotlin.sendFakeInfectedRequest(context, ExposeeAuthMethodAuthorization(getAuthorizationHeader(accessToken)))
+
+ //Execute Onset Date Request
+ authCodeRepository.getOnsetDate(AuthenticationCodeRequestModel(FAKE_AUTH_CODE, 1))
+
+ //TODO: Insert Delay
+
+ //Execute Access Token Request
+ val accessTokenResponse = authCodeRepository.getAccessToken(AuthenticationCodeRequestModel(FAKE_AUTH_CODE, 1))
+ val dp3tAccessToken = accessTokenResponse.dp3TAccessToken.accessToken
+ //Execute DP3T Infected Request
+ DP3TKotlin.sendFakeInfectedRequest(context, ExposeeAuthMethodAuthorization(getAuthorizationHeader(dp3tAccessToken)))
+ //Execute Checkin UserUpload Request
+ val checkinAccessToken = accessTokenResponse.checkInAccessToken.accessToken
+ UserUploadRepository().fakeUserUpload(getAuthorizationHeader(checkinAccessToken))
true
- } catch (e: Exception) {
+ } catch (e: Throwable) {
Logger.e(TAG, "fake request failed", e)
false
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
index 26a9956f0..83b9e9a28 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthCodeModels.kt
@@ -9,4 +9,7 @@ data class AuthenticationCodeResponseModelV2(
val dp3TAccessToken: AuthenticationCodeResponseModel
)
+class AuthenticationCodeResponseModel(val accessToken: String)
+
+
data class OnsetResponse(val onset: String?)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModel.java b/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModel.java
deleted file mode 100644
index ddcd7570e..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/AuthenticationCodeResponseModel.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.networking.models;
-
-public class AuthenticationCodeResponseModel {
-
- private String accessToken;
-
- public String getAccessToken() {
- return accessToken;
- }
-
-}
From 45e50b6a1fa19536c3c2b08d21387c8997b3173d Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 16:30:13 +0200
Subject: [PATCH 079/207] Minor NothThankYou screen fix
---
.../res/layout/fragment_not_thank_you.xml | 9 +
app/src/main/res/values-bs/strings.xml | 69 +++-----
app/src/main/res/values-de/strings.xml | 161 ++++++++----------
app/src/main/res/values-es/strings.xml | 69 +++-----
app/src/main/res/values-fr/strings.xml | 69 +++-----
app/src/main/res/values-hr/strings.xml | 69 +++-----
app/src/main/res/values-it/strings.xml | 69 +++-----
app/src/main/res/values-pt/strings.xml | 69 +++-----
app/src/main/res/values-rm/strings.xml | 69 +++-----
app/src/main/res/values-sq/strings.xml | 69 +++-----
app/src/main/res/values-sr/strings.xml | 69 +++-----
app/src/main/res/values-ti/strings.xml | 69 +++-----
app/src/main/res/values-tr/strings.xml | 69 +++-----
app/src/main/res/values/strings.xml | 69 +++-----
14 files changed, 379 insertions(+), 619 deletions(-)
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 aef45eb70..f9c12b340 100644
--- a/app/src/main/res/layout/fragment_not_thank_you.xml
+++ b/app/src/main/res/layout/fragment_not_thank_you.xml
@@ -61,6 +61,15 @@
android:paddingHorizontal="@dimen/spacing_huge"
android:text="@string/not_thank_you_screen_text2" />
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1582,7 +1543,6 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
@@ -1649,8 +1609,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
+
+
@@ -1733,10 +1693,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
@@ -1789,4 +1747,27 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d492b6f05..1cc226dbb 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -104,7 +104,7 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Mögliche Ansteckung"
- "Es besteht die Möglichkeit einer Ansteckung."
+ "Sie könnten sich angesteckt haben""App aktiv"
@@ -260,13 +260,13 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Befolgen Sie die erhaltenen Anweisungen zur Isolation"
- "Tracing und Check-ins deaktiviert"
- "Es werden keine Begegnungen mehr gespeichert."
+ "Tracing deaktiviert"
+ "Es werden keine Begegnungen gespeichert""Kontaktieren Sie die""Befolgen Sie die"
- "Anweisungen zur Selbstisolation"
+ "Anweisungen zur Isolation""Neue Meldung"
@@ -276,7 +276,7 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Positiv getestet"
- "Es besteht die Möglichkeit einer Ansteckung."
+ "Sie könnten sich angesteckt haben""Sie wurden positiv auf das Coronavirus getestet."
@@ -335,7 +335,7 @@ Schritt voraus"
"Mit der SwissCovid App können alle dazu beitragen, die Verbreitung des Coronavirus einzudämmen."
- "Die App informiert Sie, wenn Sie potenziell dem Coronavirus ausgesetzt waren."
+ "Die App warnt Sie, wenn Sie potenziell dem Coronavirus ausgesetzt waren.""Privacy by Design"
@@ -350,7 +350,7 @@ Schritt voraus"
"Die gespeicherten zufälligen IDs bleiben auf Ihrem Mobiltelefon und werden nach 14 Tagen gelöscht."
- "Wie funktioniert die App?"
+ "Begegnungen""Begegnungen erkennen
@@ -370,7 +370,7 @@ dank Bluetooth"
möglichen Ansteckung"
- "Die App informiert Sie, wenn Sie potenziell dem Coronavirus ausgesetzt waren."
+ "Die App warnt Sie, wenn Sie potenziell dem Coronavirus ausgesetzt waren.""Mit dem richtigen Verhalten können Sie Infektionsketten unterbrechen und andere schützen."
@@ -407,7 +407,7 @@ möglichen Ansteckung"
"Mitteilungen erlauben"
- "Damit Sie von der App informiert werden, müssen Sie Mitteilungen erlauben."
+ "Damit die App Sie warnen kann, müssen Sie Mitteilungen erlauben.""Mitteilungen erlauben"
@@ -430,7 +430,7 @@ Halten Sie sich an die erhaltenen Anweisungen zur Isolation."
"Die App ist eingerichtet"
- "Vielen Dank, dass Sie mithelfen, Infektionsketten zu unterbrechen."
+ "Vielen Dank, dass Sie mithelfen, die Verbreitung des Coronavirus einzudämmen.""Starten"
@@ -457,7 +457,7 @@ Halten Sie sich an die erhaltenen Anweisungen zur Isolation."
"Erneuter Verdacht"
- "Es besteht die Möglichkeit einer Ansteckung."
+ "Sie könnten sich angesteckt haben""Uhrzeit Fehler"
@@ -692,7 +692,7 @@ Zudem sind folgende Symptome möglich:
"Warum ist das Tracing deaktiviert?"
- "Nach einem positiven Test wird das Tracing in der App automatisch deaktiviert. Das Tracing kann wieder aktiviert werden, sobald Sie die Isolation beendet haben."
+ "Nach einem positiven Testergebnis wird das Tracing in der App automatisch deaktiviert. Das Tracing kann wieder aktiviert werden, sobald Sie die Isolation beendet haben.""de"
@@ -736,7 +736,7 @@ Zudem sind folgende Symptome möglich:
- "Um die App nutzen zu können, müssen {TRACING_SETTING_TEXT} aktiviert werden."
+ "Die Tracing-Funktion muss aktiviert sein, damit Begegnungen erkannt werden.""Aktivieren"
@@ -772,7 +772,7 @@ Zudem sind folgende Symptome möglich:
"Möchten Sie die Isolation wirklich beenden?""Akku-Optimierung ignorieren"
- "Um Begegnungen immer erkennen zu können, muss die Akku-Optimierung für diese App ausgeschaltet sein."
+ "Damit die App funktioniert, muss die Akku-Optimierung für diese App ausgeschaltet sein.""Zulassen""OK""Nur für diese App"
@@ -840,9 +840,8 @@ Falls auf Ihrem Mobiltelefon zurzeit kein Update verfügbar ist, versuchen Sie e
"SwissCovid bleibt aktiv, auch wenn die App geschlossen wird."
- "SwissCovid
-stoppt
-Infektionsketten."
+ "SwissCovid –
+bei einem Ansteckungsrisiko schnell informiert.""Obwohl keine Daten zu Ihrer Person gesendet werden, kann es sein, dass sich jemand anhand des Datums an die Begegnung erinnern kann.""Bleibe ich anonym?"
@@ -854,7 +853,7 @@ Infektionsketten."
"COVID-19-Benachrichtigungen"
- "Um die App nutzen zu können, müssen COVID-19 Benachrichtigungen aktiviert werden."
+ "Die Tracing-Funktion muss aktiviert sein, damit Begegnungen erkannt werden.""SwissCovid"
@@ -1544,7 +1543,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Check out"
- "Check-ins"
+ "Check-in""Check-In"
@@ -1571,10 +1570,10 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Bearbeiten"
- "Noch keine Tagebucheinträge vorhanden"
+ "Noch keine Einträge vorhanden"
- "Wenn Sie einen Swiss-Covid-QR-Code scannen, um sich vor Ort einzuchecken, wird hier automatisch ein Eintrag angelegt."
+ "Wenn Sie einen SwissCovid-QR-Code scannen, um sich vor Ort einzuchecken, wird hier automatisch ein Eintrag angelegt.""SwissCovid benötigt Zugriff auf die Kamera Ihres Gerätes. Die Kamera wird nur verwendet, um QR Codes zu scannen."
@@ -1586,48 +1585,8 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"z.B. Raum oder Anlassname"
-
-
- "Kategorie"
-
-
- "Andere"
-
-
- "Sitzungsraum"
-
-
- "Kafeteria"
-
-
- "Privater Event"
-
-
- "Titel"
-
"PDF drucken"
-
-
- "Kantine"
-
-
- "Bibliothek"
-
-
- "Vorlesungssaal"
-
-
- "Laden"
-
-
- "Fitnesscenter"
-
-
- "Küchenbereich"
-
-
- "Büroräume""Info""Netzwerk Fehler""Überprüfen Sie Ihre Internet Verbindung."
@@ -1644,7 +1603,6 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Dieser QR Code ist nicht kompatibel mit der installierten App Version. Führen Sie ein Update der App durch, um den QR Code zu scannen.""Update benötigt""Jetzt updaten"
- "CrowdNotifier Meldungen""Check out""Snooze""Permanente Checkin Meldung"
@@ -1658,10 +1616,10 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"QR-Code scannen"
- "Check-Ins deaktiviert"
+ "Check-In deaktiviert"
- "Neue Check-Ins sind nicht möglich."
+ "Derzeit ist kein Check-in möglich""Scannen Sie einen SwissCovid-QR-Code, um sich vor Ort einzuchecken."
@@ -1676,7 +1634,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Mehr zum Covidcode"
- "Entfernen"
+ "Eintrag entfernen""Löschen"
@@ -1706,23 +1664,24 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Das Teilen Ihrer privaten Schlüssel hilft «SwissCovid», andere potenziell infizierte Personen zu benachrichtigen und die weitere Ausbreitung des Virus zu verhindern. Wenn Sie Ihre privaten Schlüssel nicht teilen, werden keine potenziell infizierten Personen benachrichtigt."
- "Achtung"
+ "Eintrag entfernen"
- "Sind Sie sicher, dass Sie “{LOCATION_INFO}” in Ihrem Tagebuch nicht mehr anzeigen möchten? Diese Aktion kann nicht rückgängig gemacht werden."
+ "Ihr Eintrag wird unwiderruflich aus der Liste entfernt."
- "Nicht mehr anzeigen"
+ "Vollständig entfernen"
-
- "Die App speichert diesen Eintrag noch für 14 Tage verschlüsselt im Hintergrund, damit Sie bei einer möglichen Ansteckung trotzdem gewarnt werden können."
+
+
+ "Nur ausblenden und weiterhin benachrichtigt werden?""Positiv getestet?"
- "Begegnungen und Check-ins wieder aktivieren."
+ "Begegnungen und Check-in wieder aktivieren.""{HOURS} h {MINUTES}'"
@@ -1737,10 +1696,10 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Möchten Sie den QR Code wirklich löschen?"
- "Selbst einchecken"
+ "Self Check-In"
- "Schützen Sie sich und andere indem Sie bei Events ein- und auschecken."
+ "Scannen Sie einen SwissCovid-QR-Code, um sich vor Ort einzuchecken.""Sichern nicht möglich"
@@ -1783,7 +1742,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
zu hinterlassen"
- "Geben Sie hier ihren CovidCode ein"
+ "Geben Sie hier Ihren Covidcode ein, um andere zu warnen.""Isolation beenden?"
@@ -1801,26 +1760,23 @@ zu hinterlassen"
"Keine zentrale Datenspeicherung"
- "Für die SwissCovid-Check-ins müssen keine persönlichen Daten wie Name, Adresse oder Telefonnummer angegeben werden und es werden auch keine GPS-Standortdaten erfasst. Die Liste der Check-ins ist nur lokal auf Ihrem Smartphone gespeichert und wird an keinen zentralen Server gesendet. Nach 14 Tagen werden die lokalen Daten wieder gelöscht."
+ "Für die SwissCovid-Check-ins müssen keine persönlichen Daten wie Name, Adresse oder Telefonnummer angegeben werden und es werden auch keine GPS-Standortdaten erfasst. Die Liste der Check-ins wird lokal auf Ihrem Smartphone gespeichert. Nach 14 Tagen werden die lokalen Daten wieder gelöscht.""Erstellen Sie einen QR-Code"
- "Mit einem SwissCovid-QR-Code, können sich Personen vor Ort einchecken, ohne Ihre Daten zu hinterlassen."
+ "Mit einem SwissCovid-QR-Code können sich Personen vor Ort einchecken, ohne ihre Daten zu hinterlassen.""Kein Ersatz für die obligatorische Kontaktdatenerfassung"
- "SwissCovid-Check-ins sind kein Ersatz für die in der Covid-19-Verordnung vorgeschriebene Kontaktdatenerfassungspflicht."
+ "SwissCovid-Check-ins sind kein Ersatz für die in der «Covid-19-Verordnung besondere Lage» vorgeschriebene Kontaktdatenerfassungspflicht.""Wo können Check-ins eingesetzt werden?""Warum eine Check-in-Funktion?"
- "Ersetzen SwissCovid-Check-ins die Pflicht zur Kontaktdatenerfassung?"
- "SwissCovid-Check-ins können dort eingesetzt werden, wo Menschen
-sich treffen aber keine Pflicht zur Kontaktdatenerfassung besteht.
+ "SwissCovid-Check-ins können dort eingesetzt werden, wo sich Menschen treffen, aber keine Pflicht zur Kontaktdatenerfassung besteht.
Mögliche Einsatzorte:
– Private Anlässe
-– Vereinsanlässe
+– Vereinsanlässe
– Zusammenkünfte im professionellen Umfeld wie z.B. Sitzungszimmer, Büroräume, Kantinen, etc."
- "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Sie berücksichtigt nicht nur enge Kontakte, sondern auch solche, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben – unabhängig vom Abstand."
- "Nein, SwissCovid-Check-ins ersetzen die Pflicht zur Kontaktdatenerfassung, wie sie in der Covid-19-Verordnung festgehalten ist, nicht. Sie dienen lediglich als Ergänzung, um Teilnehmende rasch über eine mögliche Ansteckung zu informieren."
+ "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Sie berücksichtigt nicht nur enge Kontakte, sondern auch solche, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben – unabhängig vom Abstand. "
- "Mögliche Risikosituation:"
+ "Risikosituation""Begegnungen"
@@ -1844,18 +1800,18 @@ Mögliche Einsatzorte:
"Contact Tracing QR Code"
- "Nach einem positiven Test sind die Check-ins automatisch deaktiviert. Sie können nach Beendigung Ihrer Isolation wieder aktiviert werden."
+ "Nach einem positiven Testergebnis ist die Check-in-Funktion automatisch deaktiviert. Sie kann nach Beendigung Ihrer Isolation wieder aktiviert werden."
- "Vergeben Sie einen Titel, der den Ort
-oder Ihren Anlass näher beschreibt."
+ "Vergeben Sie einen Titel, der den Ort oder Ihren Anlass näher beschreibt.
+Dieser Titel wird beim Check-in angezeigt.""Was können Sie tun?""Kontakte schützen""Auf Symptome achten""Testen lassen"
- "Sie könnten bereits ansteckend sein, ohne es zu merken. Beachten Sie die geltenden Hygiene- und Schutz-massnahmen und schützen Sie Ihre Familie, Ihre Freunde und Ihr Umfeld."
- "Überwachen Sie Ihren Gesundheitszustand und lassen Sie sich beim Auftreten von Symptomen sofort testen."
- "Auch wenn keine Symptome auftreten, ist ein Corona-Test sinnvoll und empfohlen (ab dem 5. Tag nach dem angezeigten Kontaktdatum)."
+ "Sie könnten bereits ansteckend sein, ohne es zu merken. Beachten Sie die geltenden Hygiene- und Schutzmassnahmen und schützen Sie Ihre Familie, Ihre Freunde und Ihr Umfeld."
+ "Überwachen Sie Ihren Gesundheitszustand."
+ "Lassen Sie sich beim Auftreten von Symptomen sofort testen. Auch wenn keine Symptome auftreten, ist ein Corona-Test sinnvoll und empfohlen."
@@ -1871,9 +1827,32 @@ oder Ihren Anlass näher beschreibt.""Keine Daten senden"
- "Es wurden keine Check-In-Einträge gesendet. Es wird niemand benachrichtigt."
+ "Es wurden keine Check-In-Einträge gesendet.""Auswahl teilen""Nichts teilen"
+ "Einchecken ohne Daten zu hinterlassen"
+ "Check-ins"
+ "Mit SwissCovid-Check-ins können Sie sich mit der App an einem Ort oder bei einem Treffen einchecken."
+ "Ihre Anwesenheit wird nur in der App gespeichert. Sie müssen keine persönlichen Daten in der App hinterlassen."
+ "Mit SwissCovid-Check-ins können Sie sich mit der App vor Ort einchecken. Ihre Anwesenheit wird nur in der App gespeichert. Sie müssen keine persönlichen Daten in der App hinterlassen. Dazu wurden die Nutzungsbedingungen und Datenschutzerklärung aktualisiert."
+ "Überspringen"
+
+
+ "Der Eintrag wird 14 Tage verschlüsselt im Hintergrund der App gespeichert. Bei einer möglichen Ansteckung können Sie so trotzdem benachrichtigt werden."
+
+
+ "Nur ausblenden"
+ "Die App kann Sie erinnern, zum richtigen Zeitpunkt wieder auszuchecken."
+
+
+ "Oder vollständig entfernen?"
+
+
+ "Der Eintrag wird vollständig entfernt. Sie können nicht mehr gewarnt werden."
+ "Es wird niemand benachrichtigt."
+
+
+ "Was muss ich übers Testen wissen?"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index b043dfcb0..0bfb0c2a4 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -1516,47 +1516,8 @@ Contacte con la Infoline del coronavirus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1573,7 +1534,6 @@ Contacte con la Infoline del coronavirus:"
-
@@ -1640,8 +1600,8 @@ Contacte con la Infoline del coronavirus:"
-
-
+
+
@@ -1724,10 +1684,8 @@ Contacte con la Infoline del coronavirus:"
-
-
@@ -1780,4 +1738,27 @@ Contacte con la Infoline del coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index ab506dc42..4b57fddd2 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1551,47 +1551,8 @@ Contactez l'infoline coronavirus :"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1608,7 +1569,6 @@ Contactez l'infoline coronavirus :"
-
@@ -1675,8 +1635,8 @@ Contactez l'infoline coronavirus :"
-
-
+
+
@@ -1759,10 +1719,8 @@ Contactez l'infoline coronavirus :"
-
-
@@ -1815,4 +1773,27 @@ Contactez l'infoline coronavirus :"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 16d803b2b..00458a0ff 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -1521,47 +1521,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1578,7 +1539,6 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
@@ -1645,8 +1605,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
+
+
@@ -1729,10 +1689,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
@@ -1785,4 +1743,27 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 5b14459a4..8d2c5c9cb 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1559,47 +1559,8 @@ Contatta la Infoline coronavirus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1616,7 +1577,6 @@ Contatta la Infoline coronavirus:"
-
@@ -1683,8 +1643,8 @@ Contatta la Infoline coronavirus:"
-
-
+
+
@@ -1767,10 +1727,8 @@ Contatta la Infoline coronavirus:"
-
-
@@ -1823,4 +1781,27 @@ Contatta la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 0f7f59809..a07b8e182 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1526,47 +1526,8 @@ Contacte a linha de informação coronavírus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1583,7 +1544,6 @@ Contacte a linha de informação coronavírus:"
-
@@ -1650,8 +1610,8 @@ Contacte a linha de informação coronavírus:"
-
-
+
+
@@ -1734,10 +1694,8 @@ Contacte a linha de informação coronavírus:"
-
-
@@ -1790,4 +1748,27 @@ Contacte a linha de informação coronavírus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 622d10270..2ca22c683 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -1521,47 +1521,8 @@ Alura contactai la Infoline coronavirus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1578,7 +1539,6 @@ Alura contactai la Infoline coronavirus:"
-
@@ -1645,8 +1605,8 @@ Alura contactai la Infoline coronavirus:"
-
-
+
+
@@ -1729,10 +1689,8 @@ Alura contactai la Infoline coronavirus:"
-
-
@@ -1785,4 +1743,27 @@ Alura contactai la Infoline coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 22641f8e9..f422d6820 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -1526,47 +1526,8 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1583,7 +1544,6 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
-
@@ -1650,8 +1610,8 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
-
-
+
+
@@ -1734,10 +1694,8 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
-
-
@@ -1790,4 +1748,27 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 48a0dbf3f..09df5bb84 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -1521,47 +1521,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1578,7 +1539,6 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
@@ -1645,8 +1605,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
+
+
@@ -1729,10 +1689,8 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
-
-
@@ -1785,4 +1743,27 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index cfc557ac7..01316aaea 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -1501,47 +1501,8 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1558,7 +1519,6 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
-
@@ -1625,8 +1585,8 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
-
-
+
+
@@ -1709,10 +1669,8 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
-
-
@@ -1765,4 +1723,27 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 938b5d160..72797e67e 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -1503,47 +1503,8 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1560,7 +1521,6 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
-
@@ -1627,8 +1587,8 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
-
-
+
+
@@ -1711,10 +1671,8 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
-
-
@@ -1767,4 +1725,27 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 24c33d04f..ba0b5a3f2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1548,47 +1548,8 @@ If so, contact the Coronavirus Infoline:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1605,7 +1566,6 @@ If so, contact the Coronavirus Infoline:"
-
@@ -1672,8 +1632,8 @@ If so, contact the Coronavirus Infoline:"
-
-
+
+
@@ -1756,10 +1716,8 @@ If so, contact the Coronavirus Infoline:"
-
-
@@ -1812,4 +1770,27 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From e5e6a3b4afbb224ef4530009fbdbd5d80b81d298 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 25 May 2021 12:06:12 +0200
Subject: [PATCH 080/207] Fix string issues
---
.../java/ch/admin/bag/dp3t/extensions/VenueTypeExtensions.kt | 2 +-
app/src/main/res/layout/dialog_fragment_hide_in_diary.xml | 2 +-
app/src/main/res/layout/fragment_generate_qr_code.xml | 2 +-
app/src/main/res/layout/item_events_footer.xml | 4 ++--
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueTypeExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueTypeExtensions.kt
index 9706fe1f8..da561ca51 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueTypeExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueTypeExtensions.kt
@@ -9,6 +9,6 @@ fun VenueType.getNameRes(): Int {
return when (this) {
VenueType.USER_QR_CODE -> R.string.venue_type_user_qr_code
VenueType.CONTACT_TRACING_QR_CODE -> R.string.venue_type_contact_tracing_qr_code
- else -> R.string.web_generator_category_other
+ else -> 0
}
}
\ No newline at end of file
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 0b9d12d44..fe5bd3b60 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
@@ -56,7 +56,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_large"
- android:text="@string/remove_diary_warning_star_text" />
+ android:text="@string/remove_diary_warning_text" />
+ android:text="TODO" />
+ android:text="TODO" />
From db9ab2c95263b3711551ca31500dbc609c123b1e Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 21 May 2021 11:44:28 +0200
Subject: [PATCH 081/207] Implemented new Onboarding
---
.../onboarding/OnboardingContentFragment.java | 91 ------------------
.../onboarding/OnboardingContentFragment.kt | 70 ++++++++++++++
.../OnboardingSlidePageAdapter.java | 86 -----------------
.../onboarding/OnboardingSlidePageAdapter.kt | 60 ++++++++++++
app/src/main/res/drawable/ill_checkins.xml | 96 +++++++++++++++++++
.../layout/fragment_onboarding_content.xml | 50 +---------
.../main/res/layout/item_icon_with_text.xml | 26 +++++
7 files changed, 255 insertions(+), 224 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.kt
create mode 100644 app/src/main/res/drawable/ill_checkins.xml
create mode 100644 app/src/main/res/layout/item_icon_with_text.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.java b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.java
deleted file mode 100644
index da3d194d2..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.onboarding;
-
-import android.content.res.ColorStateList;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.fragment.app.Fragment;
-
-import ch.admin.bag.dp3t.R;
-
-public class OnboardingContentFragment extends Fragment {
-
- private static final String ARG_RES_TITLE = "RES_TITLE";
- private static final String ARG_RES_SUBTITLE = "RES_SUBTITLE";
- private static final String ARG_RES_DESCRIPTION_1 = "RES_DESCRIPTION_1";
- private static final String ARG_RES_DESCRIPTION_2 = "RES_DESCRIPTION_2";
- private static final String ARG_RES_DESCR_ICON_1 = "ARG_RES_DESCR_ICON_1";
- private static final String ARG_RES_DESCR_ICON_2 = "ARG_RES_DESCR_ICON_2";
- private static final String ARG_RES_ILLUSTRATION = "RES_ILLUSTRATION";
- private static final String ARG_STYLE_GREEN = "ARG_STYLE_GREEN";
-
- public static OnboardingContentFragment newInstance(@StringRes int title, @StringRes int subtitle,
- @DrawableRes int illustration, @StringRes int description1, @DrawableRes int iconDescription1,
- @StringRes int description2, @DrawableRes int iconDescription2, boolean greenStyle) {
- Bundle args = new Bundle();
- args.putInt(ARG_RES_TITLE, title);
- args.putInt(ARG_RES_SUBTITLE, subtitle);
- args.putInt(ARG_RES_ILLUSTRATION, illustration);
- args.putInt(ARG_RES_DESCR_ICON_1, iconDescription1);
- args.putInt(ARG_RES_DESCRIPTION_1, description1);
- args.putInt(ARG_RES_DESCR_ICON_2, iconDescription2);
- args.putInt(ARG_RES_DESCRIPTION_2, description2);
- args.putBoolean(ARG_STYLE_GREEN, greenStyle);
- OnboardingContentFragment fragment = new OnboardingContentFragment();
- fragment.setArguments(args);
- return fragment;
- }
-
- public OnboardingContentFragment() {
- super(R.layout.fragment_onboarding_content);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- Bundle args = requireArguments();
-
- ((TextView) view.findViewById(R.id.onboarding_title)).setText(args.getInt(ARG_RES_TITLE));
-
- TextView subtitle = view.findViewById(R.id.onboarding_subtitle);
- subtitle.setText(args.getInt(ARG_RES_SUBTITLE));
- if (args.getBoolean(ARG_STYLE_GREEN))
- subtitle.setTextColor(getResources().getColor(R.color.green_main, null));
-
- ((ImageView) view.findViewById(R.id.onboarding_illustration)).setImageResource(args.getInt(ARG_RES_ILLUSTRATION));
-
- ImageView icon1 = view.findViewById(R.id.onboarding_description_1_icon);
- icon1.setImageResource(args.getInt(ARG_RES_DESCR_ICON_1));
- if (args.getBoolean(ARG_STYLE_GREEN))
- icon1.setImageTintList(ColorStateList.valueOf(getResources().getColor(R.color.green_main, null)));
- ((TextView) view.findViewById(R.id.onboarding_description_1)).setText(args.getInt(ARG_RES_DESCRIPTION_1));
-
- ImageView icon2 = view.findViewById(R.id.onboarding_description_2_icon);
- icon2.setImageResource(args.getInt(ARG_RES_DESCR_ICON_2));
- if (args.getBoolean(ARG_STYLE_GREEN))
- icon2.setImageTintList(ColorStateList.valueOf(getResources().getColor(R.color.green_main, null)));
- ((TextView) view.findViewById(R.id.onboarding_description_2)).setText(args.getInt(ARG_RES_DESCRIPTION_2));
-
- 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/onboarding/OnboardingContentFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.kt
new file mode 100644
index 000000000..227ca308b
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingContentFragment.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020 Ubique Innovation AG
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package ch.admin.bag.dp3t.onboarding
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.databinding.FragmentOnboardingContentBinding
+import ch.admin.bag.dp3t.databinding.ItemIconWithTextBinding
+
+private const val ARG_RES_TITLE = "ARG_RES_TITLE"
+private const val ARG_RES_SUBTITLE = "ARG_RES_SUBTITLE"
+private const val ARG_RES_DESCRIPTIONS = "ARG_RES_DESCRIPTIONS"
+private const val ARG_RES_DESCR_ICONS = "ARG_RES_DESCR_ICONS"
+private const val ARG_RES_ILLUSTRATION = "ARG_RES_ILLUSTRATION"
+
+class OnboardingContentFragment : Fragment() {
+
+ companion object {
+
+ fun newInstance(
+ @StringRes title: Int,
+ @StringRes subtitle: Int,
+ @DrawableRes illustration: Int,
+ @StringRes descriptions: IntArray,
+ @DrawableRes icons: IntArray
+ ) = OnboardingContentFragment().apply {
+ arguments = bundleOf(
+ ARG_RES_TITLE to title,
+ ARG_RES_SUBTITLE to subtitle,
+ ARG_RES_ILLUSTRATION to illustration,
+ ARG_RES_DESCRIPTIONS to descriptions,
+ ARG_RES_DESCR_ICONS to icons
+ )
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return FragmentOnboardingContentBinding.inflate(inflater).apply {
+ val args = requireArguments()
+ onboardingTitle.setText(args.getInt(ARG_RES_TITLE))
+ onboardingSubtitle.setText(args.getInt(ARG_RES_SUBTITLE))
+ onboardingIllustration.setImageResource(args.getInt(ARG_RES_ILLUSTRATION))
+
+ val descriptions = args.getIntArray(ARG_RES_DESCRIPTIONS) ?: intArrayOf()
+ val icons = args.getIntArray(ARG_RES_DESCR_ICONS) ?: intArrayOf()
+ for ((description, icon) in descriptions.zip(icons)) {
+ ItemIconWithTextBinding.inflate(inflater, descriptionContainer, true).apply {
+ this.icon.setImageResource(icon)
+ this.text.setText(description)
+ }
+ }
+
+ onboardingContinueButton.setOnClickListener { (activity as OnboardingActivity?)!!.continueToNextPage() }
+ }.root
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.java b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.java
deleted file mode 100644
index d1ca78f23..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2020 Ubique Innovation AG
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * SPDX-License-Identifier: MPL-2.0
- */
-package ch.admin.bag.dp3t.onboarding;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.viewpager2.adapter.FragmentStateAdapter;
-
-import ch.admin.bag.dp3t.R;
-
-public class OnboardingSlidePageAdapter extends FragmentStateAdapter {
-
- public OnboardingSlidePageAdapter(FragmentActivity fragmentActivity) {
- super(fragmentActivity);
- }
-
- @NonNull
- @Override
- public Fragment createFragment(int position) {
- switch (position) {
- case 0:
- return OnboardingContentFragment.newInstance(
- R.string.onboarding_prinzip_title,
- R.string.onboarding_prinzip_heading,
- R.drawable.ill_prinzip,
- R.string.onboarding_prinzip_text1,
- R.drawable.ic_begegnungen,
- R.string.onboarding_prinzip_text2,
- R.drawable.ic_message_alert,
- false);
- case 1:
- return OnboardingContentFragment.newInstance(
- R.string.onboarding_privacy_title,
- R.string.onboarding_privacy_heading,
- R.drawable.ill_privacy,
- R.string.onboarding_privacy_text1,
- R.drawable.ic_key,
- R.string.onboarding_privacy_text2,
- R.drawable.ic_lock,
- true);
- case 2:
- return OnboardingContentFragment.newInstance(
- R.string.onboarding_begegnungen_title,
- R.string.onboarding_begegnungen_heading,
- R.drawable.ill_bluetooth,
- R.string.onboarding_begegnungen_text1,
- R.drawable.ic_begegnungen,
- R.string.onboarding_begegnungen_text2,
- R.drawable.ic_bluetooth,
- false);
- case 3:
- return OnboardingContentFragment.newInstance(
- R.string.onboarding_meldung_title,
- R.string.onboarding_meldung_heading,
- R.drawable.ill_meldung,
- R.string.onboarding_meldung_text1,
- R.drawable.ic_message_alert,
- R.string.onboarding_meldung_text2,
- R.drawable.ic_home,
- false);
- case 4:
- return OnboardingDisclaimerFragment.newInstance();
- case 5:
- return OnboardingBatteryPermissionFragment.newInstance();
- case 6:
- return OnboardingGaenPermissionFragment.newInstance();
- case 7:
- return OnboardingFinishedFragment.newInstance();
- }
- throw new IllegalArgumentException("There is no fragment for view pager position " + position);
- }
-
- @Override
- public int getItemCount() {
- return 8;
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.kt
new file mode 100644
index 000000000..73fdfd751
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingSlidePageAdapter.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Ubique Innovation AG
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package ch.admin.bag.dp3t.onboarding
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import ch.admin.bag.dp3t.R
+
+class OnboardingSlidePageAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
+
+
+ private val onboardingScreens = listOf(
+ OnboardingContentFragment.newInstance(
+ R.string.onboarding_prinzip_title,
+ R.string.onboarding_prinzip_heading,
+ R.drawable.ill_prinzip,
+ intArrayOf(R.string.onboarding_prinzip_text1, R.string.onboarding_prinzip_text2),
+ intArrayOf(R.drawable.ic_begegnungen, R.drawable.ic_message_alert)
+ ),
+ OnboardingContentFragment.newInstance(
+ R.string.onboarding_checkin_title,
+ R.string.onboarding_checkin_heading,
+ R.drawable.ill_checkins,
+ intArrayOf(R.string.onboarding_checkin_text1, R.string.onboarding_checkin_text2),
+ intArrayOf(R.drawable.ic_qr, R.drawable.ic_info)
+ ),
+ OnboardingContentFragment.newInstance(
+ R.string.onboarding_begegnungen_title,
+ R.string.onboarding_begegnungen_heading,
+ R.drawable.ill_bluetooth,
+ intArrayOf(
+ R.string.onboarding_begegnungen_text1,
+ R.string.onboarding_begegnungen_text2,
+ R.string.onboarding_privacy_text1
+ ),
+ intArrayOf(R.drawable.ic_begegnungen, R.drawable.ic_bluetooth, R.drawable.ic_key)
+ ),
+ OnboardingDisclaimerFragment.newInstance(),
+ OnboardingBatteryPermissionFragment.newInstance(),
+ OnboardingGaenPermissionFragment.newInstance(),
+ OnboardingFinishedFragment.newInstance()
+ )
+
+
+ override fun createFragment(position: Int): Fragment {
+ return onboardingScreens[position]
+ }
+
+ override fun getItemCount(): Int {
+ return onboardingScreens.size
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ill_checkins.xml b/app/src/main/res/drawable/ill_checkins.xml
new file mode 100644
index 000000000..2d3075b92
--- /dev/null
+++ b/app/src/main/res/drawable/ill_checkins.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_onboarding_content.xml b/app/src/main/res/layout/fragment_onboarding_content.xml
index 4d046550a..cdb38a338 100644
--- a/app/src/main/res/layout/fragment_onboarding_content.xml
+++ b/app/src/main/res/layout/fragment_onboarding_content.xml
@@ -56,54 +56,10 @@
tools:text="Title" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:orientation="vertical" />
@@ -112,10 +68,10 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/white"
+ android:elevation="@dimen/onboarding_button_container_elevation"
android:gravity="center"
android:orientation="vertical"
android:outlineProvider="bounds"
- android:elevation="@dimen/onboarding_button_container_elevation"
android:padding="@dimen/spacing_medium">
+
+
+
+
+
+
+
From c3ba4298db5eab8586796dd406df5ca4b35e3fe9 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 21 May 2021 12:51:09 +0200
Subject: [PATCH 082/207] Implemented Update Boarding
---
...nteroperabilityUpdateBoardingFragment.java | 87 -------------------
.../UpdateBoardingActivity.java | 46 ----------
.../updateboarding/UpdateBoardingActivity.kt | 44 ++++++++++
.../updateboarding/UpdateBoardingFragment.kt | 66 ++++++++++++++
...ility.xml => fragment_update_boarding.xml} | 33 ++++---
5 files changed, 126 insertions(+), 150 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/updateboarding/InteroperabilityUpdateBoardingFragment.java
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.kt
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingFragment.kt
rename app/src/main/res/layout/{fragment_update_boarding_interoperability.xml => fragment_update_boarding.xml} (90%)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/InteroperabilityUpdateBoardingFragment.java b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/InteroperabilityUpdateBoardingFragment.java
deleted file mode 100644
index a0250d26e..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/InteroperabilityUpdateBoardingFragment.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package ch.admin.bag.dp3t.updateboarding;
-
-import android.os.Bundle;
-import android.text.Html;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
-import androidx.fragment.app.Fragment;
-
-import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.util.AssetUtil;
-import ch.admin.bag.dp3t.util.UlTagHandler;
-import ch.admin.bag.dp3t.util.UrlUtil;
-
-public class InteroperabilityUpdateBoardingFragment extends Fragment {
-
- public static InteroperabilityUpdateBoardingFragment newInstance() {
- return new InteroperabilityUpdateBoardingFragment();
- }
-
- public InteroperabilityUpdateBoardingFragment() {
- super(R.layout.fragment_update_boarding_interoperability);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- TextView termsOfUseTextview = view.findViewById(R.id.terms_of_use_textview);
- TextView dataProtectionTextView = view.findViewById(R.id.data_protection_textview);
- termsOfUseTextview.setText(Html.fromHtml(AssetUtil.getTermsOfUse(getContext()), null, new UlTagHandler()));
- dataProtectionTextView.setText(Html.fromHtml(AssetUtil.getDataProtection(getContext()), null, new UlTagHandler()));
-
- ImageView termsOfUseChevron = view.findViewById(R.id.terms_of_use_chevron_imageview);
- ImageView dataProtectionChevron = view.findViewById(R.id.data_protection_chevron_imageview);
-
- View dataProtectionToOnlineVersionButton =
- 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 termsOfUseContainer = view.findViewById(R.id.onboarding_disclaimer_terms_of_use_container);
- View dataProtectionContainer = view.findViewById(R.id.onboarding_disclaimer_data_protection_container);
-
- view.findViewById(R.id.data_protection_header_container).setOnClickListener(v -> {
- if (dataProtectionContainer.getVisibility() == View.VISIBLE) {
- dataProtectionContainer.setVisibility(View.GONE);
- v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.white));
- } else {
- dataProtectionContainer.setVisibility(View.VISIBLE);
- v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.grey_light));
- }
- dataProtectionChevron.animate()
- .rotation(dataProtectionChevron.getRotation() + 180)
- .setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime))
- .start();
- });
-
- view.findViewById(R.id.conditions_of_use_header_container).setOnClickListener(v -> {
- if (termsOfUseContainer.getVisibility() == View.VISIBLE) {
- termsOfUseContainer.setVisibility(View.GONE);
- v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.white));
- } else {
- termsOfUseContainer.setVisibility(View.VISIBLE);
- v.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.grey_light));
- }
- termsOfUseChevron.animate()
- .rotation(termsOfUseChevron.getRotation() + 180)
- .setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime))
- .start();
- });
-
- dataProtectionToOnlineVersionButton.setOnClickListener(v -> openOnlineVersion());
- termsOfUseToOnlineVersionButton.setOnClickListener(v -> openOnlineVersion());
-
- Button okButton = view.findViewById(R.id.updateboarding_ok_button);
- okButton.setOnClickListener(v -> ((UpdateBoardingActivity) getActivity()).finishUpdateBoarding());
- }
-
- private void openOnlineVersion() {
- UrlUtil.openUrl(getContext(), getString(R.string.onboarding_disclaimer_legal_button_url));
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.java b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.java
deleted file mode 100644
index 9c74d1a32..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package ch.admin.bag.dp3t.updateboarding;
-
-import android.content.Intent;
-import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentActivity;
-
-import org.dpppt.android.sdk.DP3T;
-
-import ch.admin.bag.dp3t.R;
-
-public class UpdateBoardingActivity extends FragmentActivity {
-
- // Increment this number for each new Update Boarding
- public static final int UPDATE_BOARDING_VERSION = 1;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_update_boarding);
- if (savedInstanceState == null) {
- showFirstUpdateBoardingFragment();
- }
- }
-
- private void showFirstUpdateBoardingFragment() {
- // Replace with new UpdateBoarding Fragment
- getSupportFragmentManager()
- .beginTransaction()
- .add(R.id.main_fragment_container, InteroperabilityUpdateBoardingFragment.newInstance())
- .commit();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- DP3T.onActivityResult(this, requestCode, resultCode, data);
- }
-
- public void finishUpdateBoarding() {
- setResult(RESULT_OK);
- finish();
- overridePendingTransition(R.anim.fragment_open_enter, R.anim.fragment_open_exit);
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.kt b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.kt
new file mode 100644
index 000000000..efad26818
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingActivity.kt
@@ -0,0 +1,44 @@
+package ch.admin.bag.dp3t.updateboarding
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import ch.admin.bag.dp3t.R
+import org.dpppt.android.sdk.DP3T
+
+class UpdateBoardingActivity : FragmentActivity() {
+
+
+ companion object {
+ // Increment this number for each new Update Boarding
+ const val UPDATE_BOARDING_VERSION = 2
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_update_boarding)
+ if (savedInstanceState == null) {
+ showFirstUpdateBoardingFragment()
+ }
+ }
+
+ private fun showFirstUpdateBoardingFragment() {
+ // Replace with new UpdateBoarding Fragment
+ supportFragmentManager
+ .beginTransaction()
+ .add(R.id.main_fragment_container, UpdateBoardingFragment.newInstance())
+ .commit()
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ DP3T.onActivityResult(this, requestCode, resultCode, data)
+ }
+
+ fun finishUpdateBoarding() {
+ setResult(RESULT_OK)
+ finish()
+ overridePendingTransition(R.anim.fragment_open_enter, R.anim.fragment_open_exit)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingFragment.kt
new file mode 100644
index 000000000..dc036dccc
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/updateboarding/UpdateBoardingFragment.kt
@@ -0,0 +1,66 @@
+package ch.admin.bag.dp3t.updateboarding
+
+import android.os.Bundle
+import android.text.Html
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.databinding.FragmentUpdateBoardingBinding
+import ch.admin.bag.dp3t.util.AssetUtil
+import ch.admin.bag.dp3t.util.UlTagHandler
+import ch.admin.bag.dp3t.util.UrlUtil
+
+class UpdateBoardingFragment : Fragment(R.layout.fragment_update_boarding) {
+
+
+ companion object {
+ fun newInstance() = UpdateBoardingFragment()
+ }
+
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return FragmentUpdateBoardingBinding.inflate(inflater).apply {
+ termsOfUseTextview.text = Html.fromHtml(AssetUtil.getTermsOfUse(context), null, UlTagHandler())
+ dataProtectionTextview.text = Html.fromHtml(AssetUtil.getDataProtection(context), null, UlTagHandler())
+
+ dataProtectionHeaderContainer.setOnClickListener { v ->
+
+ dataProtectionContainer.isVisible = !dataProtectionContainer.isVisible
+ v.setBackgroundColor(
+ ContextCompat.getColor(v.context, if (dataProtectionContainer.isVisible) R.color.grey_light else R.color.white)
+ )
+
+ dataProtectionChevronImageview.animate()
+ .rotation(dataProtectionChevronImageview.rotation + 180)
+ .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong())
+ .start()
+ }
+
+
+ conditionsOfUseHeaderContainer.setOnClickListener { v: View ->
+ termsOfUseContainer.isVisible = !termsOfUseContainer.isVisible
+ v.setBackgroundColor(
+ ContextCompat.getColor(v.context, if (termsOfUseContainer.isVisible) R.color.grey_light else R.color.white)
+ )
+
+ termsOfUseChevronImageview.animate()
+ .rotation(termsOfUseChevronImageview.rotation + 180)
+ .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong())
+ .start()
+ }
+ dataProtectionToOnlineVersionButton.setOnClickListener { openOnlineVersion() }
+ termsOfUseToOnlineVersionButton.setOnClickListener { openOnlineVersion() }
+ updateboardingOkButton.setOnClickListener { (activity as UpdateBoardingActivity).finishUpdateBoarding() }
+
+ }.root
+ }
+
+ private fun openOnlineVersion() {
+ UrlUtil.openUrl(context, getString(R.string.onboarding_disclaimer_legal_button_url))
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_update_boarding_interoperability.xml b/app/src/main/res/layout/fragment_update_boarding.xml
similarity index 90%
rename from app/src/main/res/layout/fragment_update_boarding_interoperability.xml
rename to app/src/main/res/layout/fragment_update_boarding.xml
index 4b1268f6b..61d2e172b 100644
--- a/app/src/main/res/layout/fragment_update_boarding_interoperability.xml
+++ b/app/src/main/res/layout/fragment_update_boarding.xml
@@ -22,9 +22,8 @@
android:background="@color/white"
android:gravity="center_horizontal"
android:orientation="vertical"
- android:paddingStart="@dimen/spacing_large"
+ android:paddingHorizontal="@dimen/spacing_large"
android:paddingTop="@dimen/spacing_huge"
- android:paddingEnd="@dimen/spacing_large"
android:paddingBottom="@dimen/spacing_large">
+ android:layout_marginVertical="@dimen/spacing_medium_large"
+ android:layout_marginTop="@dimen/spacing_medium_large"
+ android:src="@drawable/ill_checkins" />
+ android:gravity="center_horizontal"
+ android:text="@string/onboarding_checkin_title" />
+ android:text="@string/checkin_updateboarding_text" />
@@ -95,7 +96,7 @@
-
@@ -252,8 +250,9 @@
style="@style/NextStep.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/android_button_ok" />
+ android:layout_gravity="center_horizontal"
+ android:text="@string/onboarding_continue_button" />
-
+
\ No newline at end of file
From bbe9cb24f015fadc099e09d1147f70cb4703cec1 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 21 May 2021 13:22:02 +0200
Subject: [PATCH 083/207] Add don't activate button to Onboarding
---
.../onboarding/OnboardingGaenPermissionFragment.java | 7 +++++++
.../layout/fragment_onboarding_permission_gaen.xml | 12 +++++++++++-
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingGaenPermissionFragment.java b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingGaenPermissionFragment.java
index ef2916008..3a66fd967 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingGaenPermissionFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingGaenPermissionFragment.java
@@ -10,9 +10,11 @@
package ch.admin.bag.dp3t.onboarding;
import android.content.Context;
+import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
@@ -35,6 +37,7 @@ public class OnboardingGaenPermissionFragment extends Fragment {
private Button activateButton;
private Button continueButton;
+ private TextView dontActivateButton;
private AlertDialog playServicesUpdateDialog;
@@ -68,6 +71,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
continueButton.setOnClickListener(v -> {
((OnboardingActivity) requireActivity()).continueToNextPage();
});
+ dontActivateButton = view.findViewById(R.id.dont_activate_button);
+ dontActivateButton.setPaintFlags(dontActivateButton.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
+ dontActivateButton.setOnClickListener(v -> ((OnboardingActivity) requireActivity()).continueToNextPage());
}
@Override
@@ -153,6 +159,7 @@ private void updateFragmentState(boolean activated) {
PermissionButtonUtil.setButtonDefault(activateButton, R.string.onboarding_gaen_button_activate);
}
continueButton.setVisibility(activated || wasUserActive ? View.VISIBLE : View.GONE);
+ dontActivateButton.setVisibility(activated || wasUserActive ? View.GONE : View.VISIBLE);
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_onboarding_permission_gaen.xml b/app/src/main/res/layout/fragment_onboarding_permission_gaen.xml
index af54c90b2..2b9adb140 100644
--- a/app/src/main/res/layout/fragment_onboarding_permission_gaen.xml
+++ b/app/src/main/res/layout/fragment_onboarding_permission_gaen.xml
@@ -65,6 +65,17 @@
android:layout_marginTop="@dimen/spacing_small"
android:text="@string/onboarding_gaen_button_activate" />
+
+
Date: Fri, 21 May 2021 14:17:34 +0200
Subject: [PATCH 084/207] Update gradle
---
build.gradle | 2 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 29b62a76a..b551d429e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.3'
+ classpath 'com.android.tools.build:gradle:4.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index feba0ac39..4bf1d4004 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -13,4 +13,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
From ec710a1b70d456b411ef83be78d039950fcc365e Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Fri, 21 May 2021 14:47:46 +0200
Subject: [PATCH 085/207] Various UI cleanups
---
.../dp3t/checkin/CheckinOverviewFragment.kt | 1 -
.../generateqrcode/GenerateQrCodeFragment.kt | 6 +-
.../checkin/generateqrcode/QRCodeViewModel.kt | 3 +-
app/src/main/res/layout/card_checkin.xml | 2 +-
.../main/res/layout/card_generate_qr_code.xml | 3 +-
.../layout/dialog_fragment_hide_in_diary.xml | 2 +-
app/src/main/res/layout/fragment_check_in.xml | 8 +++
.../res/layout/fragment_checkin_overview.xml | 72 ++++++++++++-------
app/src/main/res/layout/fragment_qr_code.xml | 2 +-
.../main/res/layout/layout_splashboarding.xml | 4 +-
app/src/main/res/values/styles.xml | 4 ++
11 files changed, 69 insertions(+), 38 deletions(-)
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 87e78dfba..514c0a35d 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
@@ -51,7 +51,6 @@ class CheckinOverviewFragment : Fragment() {
val venueInfo = crowdNotifierViewModel.checkInState.venueInfo
crowdNotifierViewModel.startCheckInTimer()
checkinTitle.text = venueInfo.description
- checkinSubtitle.setText(venueInfo.getSubtitle())
}
})
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
index 9ff66d7c6..c65f7563d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
@@ -10,6 +10,7 @@ import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import ch.admin.bag.dp3t.databinding.FragmentGenerateQrCodeBinding
+import ch.admin.bag.dp3t.extensions.showFragment
class GenerateQrCodeFragment : Fragment() {
@@ -32,8 +33,9 @@ class GenerateQrCodeFragment : Fragment() {
}
private fun generateQrCode(title: String) {
- qrCodeViewModel.generateAndSaveQrCode(title)
- popFragmentAndHideKeyboard()
+ qrCodeViewModel.generateAndSaveQrCode(title).observe(viewLifecycleOwner) {
+ showFragment(QrCodeFragment.newInstance(it), addToBackStack = false)
+ }
}
private fun popFragmentAndHideKeyboard() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index cef97bebe..e7b2d5bc9 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -74,7 +74,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
saveGeneratedQrCode(builder.build())
}
- fun generateAndSaveQrCode(description: String) = viewModelScope.launch(Dispatchers.IO) {
+ fun generateAndSaveQrCode(description: String) = liveData(Dispatchers.IO) {
val swissCovidLocationData = SwissCovidLocationData.newBuilder()
.setVersion(SWISSCOVID_LOCATION_DATA_VERSION)
@@ -99,6 +99,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
.addGeneratedQrCodes(generatedVenueInfo.toQrCodePayload())
.build()
saveGeneratedQrCode(newWrapper)
+ emit(generatedVenueInfo)
}
fun generateQrCodeBitmapAndPdf(venueInfo: VenueInfo, qrCodeSize: Int) = viewModelScope.launch(Dispatchers.IO) {
diff --git a/app/src/main/res/layout/card_checkin.xml b/app/src/main/res/layout/card_checkin.xml
index 1378cd75d..5a8850838 100644
--- a/app/src/main/res/layout/card_checkin.xml
+++ b/app/src/main/res/layout/card_checkin.xml
@@ -92,7 +92,7 @@
@@ -34,7 +33,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_medium"
android:layout_weight="1"
- android:text="@string/events_title" />
+ android:text="@string/events_card_title" />
+ android:text="@string/remove_diary_warning_hide_text" />
+
+
+ android:visibility="gone"
+ tools:visibility="visible">
+
+
+
+
@@ -92,7 +104,8 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_small"
android:orientation="vertical"
- android:visibility="visible">
+ android:visibility="visible"
+ tools:visibility="gone">
-
-
+ tools:text="20:32" />
+ android:visibility="gone"
+ tools:visibility="visible">
@@ -167,14 +173,24 @@
android:layout_marginVertical="@dimen/spacing_large"
android:src="@drawable/ic_illu_checkin_ended" />
+
+
+
+
+ android:layout_marginTop="@dimen/spacing_medium_large"
+ android:text="@string/checkin_ended_text_detailed" />
@@ -225,7 +241,9 @@
@dimen/text_size_splash_title
@font/inter_boldbold
+ @color/dark_main
+
+
+
From 5cab8e9fd0626c0fedb8c1e406469446e1a42303 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 11:47:01 +0200
Subject: [PATCH 086/207] Remove some CrowdNotifier Styles
---
.../bag/dp3t/checkin/utils/ErrorDialog.java | 1 +
.../dialog_camera_permission_explanation.xml | 9 +--
app/src/main/res/layout/dialog_error.xml | 65 ++++++++-----------
3 files changed, 34 insertions(+), 41 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
index 1be5a623f..704658b0a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/ErrorDialog.java
@@ -24,6 +24,7 @@ public ErrorDialog(@NonNull Context context, CrowdNotifierErrorState errorState)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_error);
+ findViewById(R.id.dialog_error_container).setBackgroundResource(R.color.white);
View closeButton = findViewById(R.id.dialog_error_close_button);
closeButton.setOnClickListener(v -> dismiss());
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 6a85f4136..44981bcd6 100644
--- a/app/src/main/res/layout/dialog_camera_permission_explanation.xml
+++ b/app/src/main/res/layout/dialog_camera_permission_explanation.xml
@@ -1,6 +1,6 @@
+ android:src="@drawable/ic_close"
+ android:tint="@color/blue_main" />
-
+ android:layout_height="wrap_content">
-
-
-
+
-
-
-
-
+
+
\ No newline at end of file
From c6be11d9b83705c8ecb35ae4d63fd4a09f9767a1 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 15:58:16 +0200
Subject: [PATCH 087/207] Fix Homescreen Error Combo
---
.../main/java/ch/admin/bag/dp3t/home/HomeFragment.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
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 06f9fe725..3d311989e 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
@@ -52,6 +52,7 @@
import ch.admin.bag.dp3t.extensions.FragmentExtensionsKt;
import ch.admin.bag.dp3t.home.model.NotificationState;
import ch.admin.bag.dp3t.home.model.NotificationStateError;
+import ch.admin.bag.dp3t.home.model.TracingState;
import ch.admin.bag.dp3t.home.model.TracingStatusInterface;
import ch.admin.bag.dp3t.home.views.HeaderView;
import ch.admin.bag.dp3t.inform.InformActivity;
@@ -273,7 +274,7 @@ private void setupNotification() {
updateNotification(tracingViewModel.getAppStatusLiveData().getValue(), hasTraceKeyLoadingError));
}
- private void updateNotification(TracingStatusInterface tracingStatusInterface, boolean hasTraceKeyLoadingError) {
+ private void updateNotification(TracingStatusInterface tracingStatusInterface, boolean hasCheckinKeyLoadingError) {
//update status view
if (loadingView.getVisibility() == VISIBLE) {
loadingView.animate()
@@ -307,7 +308,8 @@ public void onAnimationEnd(Animator animation) {
}
TracingStatus.ErrorState errorState = tracingStatusInterface.getReportErrorState();
- if (errorState != null) {
+
+ if (errorState != null && tracingStatusInterface.getTracingState().equals(TracingState.ACTIVE)) {
TracingErrorStateHelper.updateErrorView(reportErrorView, errorState);
reportErrorView.findViewById(R.id.error_status_button).setOnClickListener(v -> {
loadingView.setVisibility(VISIBLE);
@@ -319,7 +321,7 @@ public void onAnimationEnd(Animator animation) {
public void onAnimationEnd(Animator animation) { tracingViewModel.sync(); }
});
});
- } else if (hasTraceKeyLoadingError) {
+ } else if (hasCheckinKeyLoadingError) {
TracingErrorStateHelper.updateErrorView(reportErrorView, CrowdNotifierErrorState.NETWORK);
reportErrorView.findViewById(R.id.error_status_button).setOnClickListener(v -> {
loadingView.setVisibility(VISIBLE);
From a51e066aee3896a4e40a4a48a6416016223d76e1 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 17:43:16 +0200
Subject: [PATCH 088/207] Change Notification Icon on Homescreen
---
.../dp3t/home/model/NotificationState.java | 21 ++++++++++++-------
.../main/res/drawable/ic_warning_round.xml | 10 +++++++++
2 files changed, 23 insertions(+), 8 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_warning_round.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java b/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
index f2471941d..ef321ad8c 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
@@ -7,7 +7,6 @@
*
* SPDX-License-Identifier: MPL-2.0
*/
-
package ch.admin.bag.dp3t.home.model;
import androidx.annotation.ColorRes;
@@ -21,7 +20,8 @@ public enum NotificationState {
EXPOSED,
POSITIVE_TESTED;
- @StringRes public static int getTitle(NotificationState notificationState) {
+ @StringRes
+ public static int getTitle(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.string.meldungen_no_meldungen_title;
@@ -33,7 +33,8 @@ public enum NotificationState {
return -1;
}
- @StringRes public static int getText(NotificationState notificationState) {
+ @StringRes
+ public static int getText(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.string.meldungen_no_meldungen_subtitle;
@@ -45,19 +46,21 @@ public enum NotificationState {
return -1;
}
- @DrawableRes public static int getIcon(NotificationState notificationState) {
+ @DrawableRes
+ public static int getIcon(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.drawable.ic_check;
case EXPOSED:
- return R.drawable.ic_info;
+ return R.drawable.ic_warning_round;
case POSITIVE_TESTED:
return R.drawable.ic_info;
}
return -1;
}
- @ColorRes public static int getTitleTextColor(NotificationState notificationState) {
+ @ColorRes
+ public static int getTitleTextColor(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.color.blue_main;
@@ -69,7 +72,8 @@ public enum NotificationState {
return -1;
}
- @ColorRes public static int geTextColor(NotificationState notificationState) {
+ @ColorRes
+ public static int geTextColor(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.color.dark_main;
@@ -81,7 +85,8 @@ public enum NotificationState {
return -1;
}
- @ColorRes public static int getBackgroundColor(NotificationState notificationState) {
+ @ColorRes
+ public static int getBackgroundColor(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.color.status_blue_bg;
diff --git a/app/src/main/res/drawable/ic_warning_round.xml b/app/src/main/res/drawable/ic_warning_round.xml
new file mode 100644
index 000000000..4db6c0736
--- /dev/null
+++ b/app/src/main/res/drawable/ic_warning_round.xml
@@ -0,0 +1,10 @@
+
+
+
From 18436572a198673f433dd75ef887d5bb6b063d80 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 18:33:35 +0200
Subject: [PATCH 089/207] Pop to CheckinOverviewFragment after self Checkin
---
.../bag/dp3t/checkin/checkinflow/CheckInFragment.kt | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
index ff5fe5c68..7b918d99d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
@@ -11,7 +11,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.activityViewModels
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.generateqrcode.QrCodeFragment
import ch.admin.bag.dp3t.checkin.models.ReminderOption
import ch.admin.bag.dp3t.checkin.utils.*
import ch.admin.bag.dp3t.databinding.FragmentCheckInBinding
@@ -99,7 +101,13 @@ class CheckInFragment : Fragment() {
}
private fun popBackToHomeFragment() {
- requireActivity().supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+ val fm = requireActivity().supportFragmentManager
+ val backStackEntry = if (fm.backStackEntryCount >= 2) fm.getBackStackEntryAt(fm.backStackEntryCount - 2) else null
+ if (backStackEntry?.name == QrCodeFragment::class.java.canonicalName) {
+ fm.popBackStack(CheckinOverviewFragment::class.java.canonicalName, 0)
+ } else {
+ fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+ }
}
}
\ No newline at end of file
From 0637e95d83c8eb252a5cf402514a5feaae95beb6 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 24 May 2021 18:44:09 +0200
Subject: [PATCH 090/207] Fix Ongoing Notification text format bug
---
.../ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
index b0c2f54d3..1b86a19c5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/NotificationHelper.java
@@ -132,7 +132,7 @@ public void startOngoingNotification(long startTime, VenueInfo venueInfo) {
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(context.getString(R.string.ongoing_notification_title)
.replace("{TIME}", StringUtil.getHourMinuteTimeString(startTime, ":")))
- .setContentText(venueInfo.getTitle() + "\n" + VenueInfoExtensionsKt.getSubtitle(venueInfo))
+ .setContentText(venueInfo.getTitle())
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.addAction(R.drawable.ic_close,
From 2d1d564e79c67c8114aacf268a4c86aeac9fe0fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Mon, 24 May 2021 14:51:33 +0200
Subject: [PATCH 091/207] updated translations
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 7 +++--
.../whattodo/WtdPositiveTestFragment.java | 7 -----
.../main/res/layout/card_enter_covidcode.xml | 9 +++++-
.../layout/card_what_to_do_checkin_report.xml | 2 +-
.../layout/card_what_to_do_save_yourself.xml | 28 ------------------
app/src/main/res/layout/fragment_check_in.xml | 2 +-
.../res/layout/fragment_generate_qr_code.xml | 12 ++++----
.../res/layout/fragment_what_to_do_test.xml | 28 ------------------
.../main/res/layout/item_events_footer.xml | 29 -------------------
.../main/res/layout/layout_splashboarding.xml | 1 +
10 files changed, 23 insertions(+), 102 deletions(-)
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 3d311989e..f539c0417 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
@@ -443,12 +443,14 @@ private void setupCheckinCardNonIsolationMode() {
private void setupCovidCodeCard() {
Button covidCodeButton = covidCodeCard.findViewById(R.id.enter_covidcode_button);
+ TextView covidCodeTitle = covidCodeCard.findViewById(R.id.enter_covidcode_title);
TextView covidCodeText = covidCodeCard.findViewById(R.id.enter_covidcode_text);
tracingViewModel.getAppStatusLiveData().observe(getViewLifecycleOwner(), tracingStatusInterface -> {
if (tracingStatusInterface.isReportedAsInfected()) {
covidCodeButton.setText(R.string.delete_infection_button);
- covidCodeText.setText(R.string.home_end_isolation_card_title);
+ covidCodeTitle.setText(R.string.home_end_isolation_card_title);
+ covidCodeText.setText(R.string.home_end_isolation_card_text);
covidCodeButton.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle);
builder.setMessage(R.string.delete_infection_dialog)
@@ -465,7 +467,8 @@ private void setupCovidCodeCard() {
});
} else {
covidCodeButton.setText(R.string.inform_code_title);
- covidCodeText.setText(R.string.home_covidcode_card_title);
+ covidCodeTitle.setText(R.string.home_covidcode_card_title);
+ covidCodeText.setText(R.string.home_covidcode_card_text);
covidCodeButton.setOnClickListener(v -> {
Intent intent = new Intent(getActivity(), InformActivity.class);
startActivity(intent);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/whattodo/WtdPositiveTestFragment.java b/app/src/main/java/ch/admin/bag/dp3t/whattodo/WtdPositiveTestFragment.java
index 101376c17..b91f77cb3 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/whattodo/WtdPositiveTestFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/whattodo/WtdPositiveTestFragment.java
@@ -47,13 +47,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
view.findViewById(R.id.wtd_inform_faq_button).setOnClickListener(v -> {
UrlUtil.openUrl(getContext(), getString(R.string.faq_button_url));
});
-
- View oldCallButton = view.findViewById(R.id.wtd_inform_call_infoline_coronavirus);
- if (oldCallButton != null) {
- oldCallButton.setOnClickListener(v -> {
- PhoneUtil.callInfolineCoronavirus(v.getContext());
- });
- }
}
private void fillContentFromConfigServer(View view) {
diff --git a/app/src/main/res/layout/card_enter_covidcode.xml b/app/src/main/res/layout/card_enter_covidcode.xml
index 79b210a4b..5c138548e 100644
--- a/app/src/main/res/layout/card_enter_covidcode.xml
+++ b/app/src/main/res/layout/card_enter_covidcode.xml
@@ -11,12 +11,19 @@
android:layout_margin="@dimen/spacing_medium_large"
android:orientation="vertical">
+
+
+ android:text="@string/home_covidcode_card_text" />
+ android:text="@string/checkin_report_link" />
-
-
-
-
-
-
-
-
-
-
+ android:text="@string/checkin_button_title" />
\ 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 00b464a76..54f5d1484 100644
--- a/app/src/main/res/layout/fragment_generate_qr_code.xml
+++ b/app/src/main/res/layout/fragment_generate_qr_code.xml
@@ -52,15 +52,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
- android:layout_marginVertical="@dimen/spacing_very_large"
+ android:layout_marginTop="@dimen/spacing_very_large"
+ android:layout_marginBottom="@dimen/spacing_medium_large"
android:text="@string/checkins_create_qr_code" />
+ android:layout_marginBottom="@dimen/spacing_very_large"
+ android:text="@string/checkins_create_qr_code_subtitle"
+ android:textAlignment="center" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_splashboarding.xml b/app/src/main/res/layout/layout_splashboarding.xml
index 4061411d9..33ccfb797 100644
--- a/app/src/main/res/layout/layout_splashboarding.xml
+++ b/app/src/main/res/layout/layout_splashboarding.xml
@@ -57,6 +57,7 @@
style="@style/NextStep.Text.SplashTitle.White"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/spacing_large"
android:gravity="center"
android:text="@string/oboarding_splashscreen_title" />
From 1b467785ba29fc0a8a2c0ca7be2106a774754612 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 25 May 2021 13:41:28 +0200
Subject: [PATCH 092/207] Reset gradle again
---
build.gradle | 2 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index b551d429e..29b62a76a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.android.tools.build:gradle:4.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4bf1d4004..feba0ac39 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -13,4 +13,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
From 66223f927e0298d93a672908d9db0d0769568ceb Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Wed, 19 May 2021 22:00:22 +0200
Subject: [PATCH 093/207] removed checkin comment field
---
.../checkin/checkinflow/CheckOutFragment.java | 6 +-----
.../checkin/diary/EditDiaryEntryFragment.java | 6 ------
.../bag/dp3t/checkin/models/DiaryEntry.java | 15 ++++-----------
.../utils/CrowdNotifierReminderHelper.java | 2 +-
.../res/layout/fragment_check_out_and_edit.xml | 17 +----------------
5 files changed, 7 insertions(+), 39 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
index 38d8c3446..9ff6ca08e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
@@ -4,7 +4,6 @@
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
-import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -41,7 +40,6 @@ public class CheckOutFragment extends Fragment {
private TextView subtitleTextView;
private View doneButton;
private View cancelButton;
- private EditText commentEditText;
private TextView fromTime;
private TextView toTime;
private TextView dateTextView;
@@ -82,7 +80,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
subtitleTextView = view.findViewById(R.id.check_out_fragment_subtitle);
doneButton = view.findViewById(R.id.check_out_fragment_done_button);
cancelButton = view.findViewById(R.id.check_out_fragment_cancel_button);
- commentEditText = view.findViewById(R.id.check_out_fragment_comment_edit_text);
fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
dateTextView = view.findViewById(R.id.check_out_fragment_date);
@@ -149,9 +146,8 @@ private void showTimePicker(boolean isFromTime) {
private void saveEntry() {
long checkIn = checkInState.getCheckInTime();
long checkOut = checkInState.getCheckOutTime();
- String comment = commentEditText.getText().toString();
long id = CrowdNotifier.addCheckIn(checkIn, checkOut, venueInfo, getContext());
- DiaryStorage.getInstance(getContext()).addEntry(new DiaryEntry(id, checkIn, checkOut, venueInfo, comment));
+ DiaryStorage.getInstance(getContext()).addEntry(new DiaryEntry(id, checkIn, checkOut, venueInfo));
viewModel.setCheckInState(null);
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
index ae6575726..a28a0a8fd 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
@@ -3,7 +3,6 @@
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
-import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -33,7 +32,6 @@ public class EditDiaryEntryFragment extends Fragment {
private TextView subtitleTextView;
private View doneButton;
private View cancelButton;
- private EditText commentEditText;
private TextView fromTime;
private TextView toTime;
private TextView dateTextView;
@@ -64,7 +62,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
subtitleTextView = view.findViewById(R.id.check_out_fragment_subtitle);
doneButton = view.findViewById(R.id.check_out_fragment_done_button);
cancelButton = view.findViewById(R.id.check_out_fragment_cancel_button);
- commentEditText = view.findViewById(R.id.check_out_fragment_comment_edit_text);
fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
dateTextView = view.findViewById(R.id.check_out_fragment_date);
@@ -77,7 +74,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
fromTime.setOnClickListener(v -> showTimePicker(true));
toTime.setOnClickListener(v -> showTimePicker(false));
- commentEditText.setText(diaryEntry.getComment());
hideInDiaryButton.setVisibility(View.VISIBLE);
hideInDiaryButton.setOnClickListener(v -> hideInDiary());
@@ -128,8 +124,6 @@ private void showTimePicker(boolean isFromTime) {
}
private void saveEntry() {
- String comment = commentEditText.getText().toString();
- diaryEntry.setComment(comment);
CrowdNotifier.updateCheckIn(diaryEntry.getId(), diaryEntry.getArrivalTime(), diaryEntry.getDepartureTime(),
diaryEntry.getVenueInfo(), getContext());
diaryStorage.updateEntry(diaryEntry);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
index b5567bd27..c180564a2 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/DiaryEntry.java
@@ -1,20 +1,21 @@
package ch.admin.bag.dp3t.checkin.models;
+import androidx.annotation.Keep;
+
import org.crowdnotifier.android.sdk.model.VenueInfo;
+@Keep
public class DiaryEntry {
private long id;
private long arrivalTime;
private long departureTime;
private VenueInfo venueInfo;
- private String comment;
- public DiaryEntry(long id, long arrivalTime, long departureTime, VenueInfo venueInfo, String comment) {
+ public DiaryEntry(long id, long arrivalTime, long departureTime, VenueInfo venueInfo) {
this.id = id;
this.arrivalTime = arrivalTime;
this.departureTime = departureTime;
this.venueInfo = venueInfo;
- this.comment = comment;
}
public long getId() {
@@ -33,10 +34,6 @@ public VenueInfo getVenueInfo() {
return venueInfo;
}
- public String getComment() {
- return comment;
- }
-
public void setArrivalTime(long arrivalTime) {
this.arrivalTime = arrivalTime;
}
@@ -45,8 +42,4 @@ public void setDepartureTime(long departureTime) {
this.departureTime = departureTime;
}
- public void setComment(String comment) {
- this.comment = comment;
- }
-
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
index 3d1fe54ff..b5e5cf3b6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/utils/CrowdNotifierReminderHelper.java
@@ -121,7 +121,7 @@ public static boolean autoCheckoutIfNecessary(Context context, CheckInState chec
long checkIn = checkInState.getCheckInTime();
long checkOut = checkIn + autoCheckoutDelay;
long id = CrowdNotifier.addCheckIn(checkIn, checkOut, checkInState.getVenueInfo(), context);
- DiaryStorage.getInstance(context).addEntry(new DiaryEntry(id, checkIn, checkOut, checkInState.getVenueInfo(), ""));
+ DiaryStorage.getInstance(context).addEntry(new DiaryEntry(id, checkIn, checkOut, checkInState.getVenueInfo()));
SecureStorage storage = SecureStorage.getInstance(context);
storage.setCheckInState(null);
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_DID_AUTO_CHECKOUT));
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 c6b61575a..008e75622 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
@@ -144,24 +144,9 @@
-
-
-
-
Date: Thu, 20 May 2021 20:43:16 +0200
Subject: [PATCH 094/207] checkout screen design
---
.../checkin/checkinflow/CheckOutFragment.java | 27 ++++++------
.../checkin/diary/EditDiaryEntryFragment.java | 11 ++---
.../layout/fragment_check_out_and_edit.xml | 41 +++++++------------
3 files changed, 36 insertions(+), 43 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
index 9ff6ca08e..c41733181 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
@@ -43,7 +43,7 @@ public class CheckOutFragment extends Fragment {
private TextView fromTime;
private TextView toTime;
private TextView dateTextView;
- private Button hideInDiaryButton;
+ private Button actionButton;
public CheckOutFragment() { super(R.layout.fragment_check_out_and_edit); }
@@ -83,7 +83,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
dateTextView = view.findViewById(R.id.check_out_fragment_date);
- hideInDiaryButton = view.findViewById(R.id.edit_diary_entry_hide_from_diary_button);
+ actionButton = view.findViewById(R.id.check_out_fragment_primary_button);
titleTextView.setText(venueInfo.getTitle());
subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(venueInfo));
@@ -94,17 +94,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
fromTime.setOnClickListener(v -> showTimePicker(true));
toTime.setOnClickListener(v -> showTimePicker(false));
- hideInDiaryButton.setVisibility(View.GONE);
-
- doneButton.setOnClickListener(v -> {
- CrowdNotifierReminderHelper.removeAllReminders(getContext());
- saveEntry();
- NotificationHelper notificationHelper = NotificationHelper.getInstance(getContext());
- notificationHelper.stopOngoingNotification();
- notificationHelper.removeReminderNotification();
- popBackToHomeFragment();
- });
+ doneButton.setOnClickListener(v -> performCheckout());
cancelButton.setOnClickListener(v -> requireActivity().getSupportFragmentManager().popBackStack());
+
+ actionButton.setText(R.string.checkout_button_title);
+ actionButton.setOnClickListener(v -> performCheckout());
}
private void refreshTimeTextViews() {
@@ -143,6 +137,15 @@ private void showTimePicker(boolean isFromTime) {
timePicker.show();
}
+ private void performCheckout() {
+ CrowdNotifierReminderHelper.removeAllReminders(getContext());
+ saveEntry();
+ NotificationHelper notificationHelper = NotificationHelper.getInstance(getContext());
+ notificationHelper.stopOngoingNotification();
+ notificationHelper.removeReminderNotification();
+ popBackToHomeFragment();
+ }
+
private void saveEntry() {
long checkIn = checkInState.getCheckInTime();
long checkOut = checkInState.getCheckOutTime();
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
index a28a0a8fd..7ee621aa9 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
@@ -3,6 +3,7 @@
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
+import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -35,7 +36,7 @@ public class EditDiaryEntryFragment extends Fragment {
private TextView fromTime;
private TextView toTime;
private TextView dateTextView;
- private View hideInDiaryButton;
+ private Button actionButton;
public EditDiaryEntryFragment() { super(R.layout.fragment_check_out_and_edit); }
@@ -65,7 +66,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
dateTextView = view.findViewById(R.id.check_out_fragment_date);
- hideInDiaryButton = view.findViewById(R.id.edit_diary_entry_hide_from_diary_button);
+ actionButton = view.findViewById(R.id.check_out_fragment_primary_button);
titleTextView.setText(diaryEntry.getVenueInfo().getTitle());
subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(diaryEntry.getVenueInfo()));
@@ -75,9 +76,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
fromTime.setOnClickListener(v -> showTimePicker(true));
toTime.setOnClickListener(v -> showTimePicker(false));
- hideInDiaryButton.setVisibility(View.VISIBLE);
- hideInDiaryButton.setOnClickListener(v -> hideInDiary());
-
doneButton.setOnClickListener(v -> {
saveEntry();
requireActivity().getSupportFragmentManager().popBackStack();
@@ -85,6 +83,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
cancelButton.setOnClickListener(v -> {
requireActivity().getSupportFragmentManager().popBackStack();
});
+
+ actionButton.setText(R.string.remove_from_diary_button);
+ actionButton.setOnClickListener(v -> hideInDiary());
}
private void refreshTimeTextViews() {
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 008e75622..866e8f399 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
@@ -11,39 +11,25 @@
+ android:paddingHorizontal="@dimen/spacing_medium_large">
-
-
@@ -51,14 +37,14 @@
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ android:paddingHorizontal="@dimen/spacing_very_large">
+
+
+ android:layout_margin="@dimen/spacing_huge"
+ tools:text="@string/remove_from_diary_button" />
From 95c9c124b831fb43a49fcbd29bfbd23b02026e40 Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Fri, 21 May 2021 15:57:59 +0200
Subject: [PATCH 095/207] converted checkout and edit fragment to kotlin
---
.../checkin/checkinflow/CheckOutFragment.java | 161 ------------------
.../checkin/checkinflow/CheckOutFragment.kt | 91 ++++++++++
.../bag/dp3t/checkin/diary/DiaryFragment.kt | 15 +-
.../checkin/diary/EditDiaryEntryFragment.java | 139 ---------------
.../checkin/diary/EditDiaryEntryFragment.kt | 72 ++++++++
.../bag/dp3t/checkin/models/CheckInState.java | 2 +-
.../layout/fragment_check_out_and_edit.xml | 74 ++------
7 files changed, 184 insertions(+), 370 deletions(-)
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
deleted file mode 100644
index c41733181..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package ch.admin.bag.dp3t.checkin.checkinflow;
-
-import android.app.TimePickerDialog;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-
-import java.util.Calendar;
-
-import org.crowdnotifier.android.sdk.CrowdNotifier;
-import org.crowdnotifier.android.sdk.model.VenueInfo;
-
-import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
-import ch.admin.bag.dp3t.checkin.models.CheckInState;
-import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
-import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
-import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper;
-import ch.admin.bag.dp3t.checkin.utils.NotificationHelper;
-import ch.admin.bag.dp3t.extensions.VenueInfoExtensionsKt;
-import ch.admin.bag.dp3t.util.StringUtil;
-
-public class CheckOutFragment extends Fragment {
-
- public static final String TAG = CheckOutFragment.class.getCanonicalName();
-
- private static final long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L;
-
- private CrowdNotifierViewModel viewModel;
- private VenueInfo venueInfo;
- private CheckInState checkInState;
-
- private TextView titleTextView;
- private TextView subtitleTextView;
- private View doneButton;
- private View cancelButton;
- private TextView fromTime;
- private TextView toTime;
- private TextView dateTextView;
- private Button actionButton;
-
- public CheckOutFragment() { super(R.layout.fragment_check_out_and_edit); }
-
- public static CheckOutFragment newInstance() {
- return new CheckOutFragment();
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- viewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
- checkInState = viewModel.getCheckInState();
- if (checkInState != null) {
- venueInfo = checkInState.getVenueInfo();
- }
- checkIfAutoCheckoutHappened();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- checkIfAutoCheckoutHappened();
- }
-
- private void checkIfAutoCheckoutHappened() {
- if (viewModel.getCheckInState() == null) {
- popBackToHomeFragment();
- }
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- titleTextView = view.findViewById(R.id.check_out_fragment_title);
- subtitleTextView = view.findViewById(R.id.check_out_fragment_subtitle);
- doneButton = view.findViewById(R.id.check_out_fragment_done_button);
- cancelButton = view.findViewById(R.id.check_out_fragment_cancel_button);
- fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
- toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
- dateTextView = view.findViewById(R.id.check_out_fragment_date);
- actionButton = view.findViewById(R.id.check_out_fragment_primary_button);
-
- titleTextView.setText(venueInfo.getTitle());
- subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(venueInfo));
-
- checkInState.setCheckOutTime(System.currentTimeMillis());
- refreshTimeTextViews();
-
- fromTime.setOnClickListener(v -> showTimePicker(true));
- toTime.setOnClickListener(v -> showTimePicker(false));
-
- doneButton.setOnClickListener(v -> performCheckout());
- cancelButton.setOnClickListener(v -> requireActivity().getSupportFragmentManager().popBackStack());
-
- actionButton.setText(R.string.checkout_button_title);
- actionButton.setOnClickListener(v -> performCheckout());
- }
-
- private void refreshTimeTextViews() {
- fromTime.setText(StringUtil.getHourMinuteTimeString(checkInState.getCheckInTime(), " : "));
- toTime.setText(StringUtil.getHourMinuteTimeString(checkInState.getCheckOutTime(), " : "));
- dateTextView.setText(StringUtil.getCheckOutDateString(getContext(), checkInState.getCheckInTime(),
- checkInState.getCheckOutTime()));
- }
-
- private void showTimePicker(boolean isFromTime) {
- Calendar time = Calendar.getInstance();
- if (isFromTime) {
- time.setTimeInMillis(checkInState.getCheckInTime());
- } else {
- time.setTimeInMillis(checkInState.getCheckOutTime());
- }
- int hour = time.get(Calendar.HOUR_OF_DAY);
- int minute = time.get(Calendar.MINUTE);
- TimePickerDialog timePicker;
- timePicker = new TimePickerDialog(getContext(), (picker, selectedHour, selectedMinute) -> {
- time.set(Calendar.HOUR_OF_DAY, selectedHour);
- time.set(Calendar.MINUTE, selectedMinute);
- if (isFromTime) {
- checkInState.setCheckInTime(time.getTimeInMillis());
- } else {
- checkInState.setCheckOutTime(time.getTimeInMillis());
- }
- if (checkInState.getCheckOutTime() < checkInState.getCheckInTime()) {
- checkInState.setCheckOutTime(checkInState.getCheckOutTime() + ONE_DAY_IN_MILLIS);
- } else if (checkInState.getCheckInTime() + ONE_DAY_IN_MILLIS < checkInState.getCheckOutTime()) {
- checkInState.setCheckOutTime(checkInState.getCheckOutTime() - ONE_DAY_IN_MILLIS);
- }
- refreshTimeTextViews();
- }, hour, minute, true);
-
- timePicker.show();
- }
-
- private void performCheckout() {
- CrowdNotifierReminderHelper.removeAllReminders(getContext());
- saveEntry();
- NotificationHelper notificationHelper = NotificationHelper.getInstance(getContext());
- notificationHelper.stopOngoingNotification();
- notificationHelper.removeReminderNotification();
- popBackToHomeFragment();
- }
-
- private void saveEntry() {
- long checkIn = checkInState.getCheckInTime();
- long checkOut = checkInState.getCheckOutTime();
- long id = CrowdNotifier.addCheckIn(checkIn, checkOut, venueInfo, getContext());
- DiaryStorage.getInstance(getContext()).addEntry(new DiaryEntry(id, checkIn, checkOut, venueInfo));
- viewModel.setCheckInState(null);
- }
-
- private void popBackToHomeFragment() {
- requireActivity().getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
- }
-
-}
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/checkinflow/CheckOutFragment.kt
new file mode 100644
index 000000000..1a802e91a
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
@@ -0,0 +1,91 @@
+package ch.admin.bag.dp3t.checkin.checkinflow
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.ViewModelProvider
+import ch.admin.bag.dp3t.R
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.models.CheckInState
+import ch.admin.bag.dp3t.checkin.models.DiaryEntry
+import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
+import ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper
+import ch.admin.bag.dp3t.checkin.utils.NotificationHelper
+import ch.admin.bag.dp3t.databinding.FragmentCheckOutAndEditBinding
+import ch.admin.bag.dp3t.extensions.getSubtitle
+import org.crowdnotifier.android.sdk.CrowdNotifier
+import org.crowdnotifier.android.sdk.model.VenueInfo
+
+class CheckOutFragment : Fragment() {
+
+ companion object {
+ private val TAG = CheckOutFragment::class.java.canonicalName
+
+ @JvmStatic
+ fun newInstance(): CheckOutFragment {
+ return CheckOutFragment()
+ }
+ }
+
+ private lateinit var viewModel: CrowdNotifierViewModel
+
+ private lateinit var venueInfo: VenueInfo
+ private lateinit var checkInState: CheckInState
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ viewModel = ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel::class.java)
+ checkIfAutoCheckoutHappened()
+ checkInState = viewModel.checkInState ?: return
+ checkInState.checkOutTime = System.currentTimeMillis()
+ venueInfo = checkInState.venueInfo
+ }
+
+ override fun onStart() {
+ super.onStart()
+ checkIfAutoCheckoutHappened()
+ }
+
+ private fun checkIfAutoCheckoutHappened() {
+ if (viewModel.checkInState == null) {
+ popBackToHomeFragment()
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return FragmentCheckOutAndEditBinding.inflate(inflater, container, false).apply {
+ checkoutTitle.text = venueInfo.title
+ checkoutSubtitle.setText(venueInfo.getSubtitle())
+
+ toolbarDoneButton.setOnClickListener { performCheckout() }
+ toolbarCancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ checkoutPrimaryButton.setText(R.string.checkout_button_title)
+ checkoutPrimaryButton.setOnClickListener { performCheckout() }
+ }.root
+ }
+
+ private fun performCheckout() {
+ CrowdNotifierReminderHelper.removeAllReminders(context)
+ saveEntry()
+ val notificationHelper = NotificationHelper.getInstance(context)
+ notificationHelper.stopOngoingNotification()
+ notificationHelper.removeReminderNotification()
+ popBackToHomeFragment()
+ }
+
+ private fun saveEntry() {
+ val checkIn = checkInState.checkInTime
+ val checkOut = checkInState.checkOutTime
+ val id = CrowdNotifier.addCheckIn(checkIn, checkOut, venueInfo, context)
+ DiaryStorage.getInstance(context).addEntry(DiaryEntry(id, checkIn, checkOut, venueInfo))
+ viewModel.checkInState = null
+ }
+
+ private fun popBackToHomeFragment() {
+ requireActivity().supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+ }
+
+}
\ No newline at end of file
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 6ac630f8a..616a158e3 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
@@ -7,7 +7,6 @@ import android.view.ViewGroup
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.CrowdNotifierViewModel
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisit
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitDayHeader
@@ -15,6 +14,7 @@ import ch.admin.bag.dp3t.checkin.diary.items.VenueVisitRecyclerItem
import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.databinding.FragmentCheckinDiaryBinding
+import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.reports.ReportsFragment
import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.model.ExposureEvent
@@ -62,24 +62,15 @@ class DiaryFragment : Fragment() {
}.root
}
-
private fun getExposureWithId(exposures: List, id: Long): ExposureEvent? {
return exposures.firstOrNull { it.id == id }
}
private fun onDiaryEntryClicked(diaryEntry: DiaryEntry, exposureEvent: ExposureEvent?) {
if (exposureEvent != null) {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, ReportsFragment.newInstance())
- .addToBackStack(ReportsFragment::class.java.canonicalName)
- .commit()
+ showFragment(ReportsFragment.newInstance())
} else {
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, EditDiaryEntryFragment.newInstance(diaryEntry.id))
- .addToBackStack(EditDiaryEntryFragment.TAG)
- .commit()
+ showFragment(EditDiaryEntryFragment.newInstance(diaryEntry.id))
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
deleted file mode 100644
index 7ee621aa9..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package ch.admin.bag.dp3t.checkin.diary;
-
-import android.app.TimePickerDialog;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import java.util.Calendar;
-
-import org.crowdnotifier.android.sdk.CrowdNotifier;
-
-import ch.admin.bag.dp3t.R;
-import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
-import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
-import ch.admin.bag.dp3t.extensions.VenueInfoExtensionsKt;
-import ch.admin.bag.dp3t.util.StringUtil;
-
-public class EditDiaryEntryFragment extends Fragment {
-
- public static final String TAG = EditDiaryEntryFragment.class.getCanonicalName();
-
- private static final long ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000L;
- private static final String ARG_DIARY_ENTRY_ID = "ARG_DIARY_ENTRY_ID";
-
- private DiaryStorage diaryStorage;
- private DiaryEntry diaryEntry;
-
- private TextView titleTextView;
- private TextView subtitleTextView;
- private View doneButton;
- private View cancelButton;
- private TextView fromTime;
- private TextView toTime;
- private TextView dateTextView;
- private Button actionButton;
-
-
- public EditDiaryEntryFragment() { super(R.layout.fragment_check_out_and_edit); }
-
- public static EditDiaryEntryFragment newInstance(long diaryEntryId) {
- EditDiaryEntryFragment fragment = new EditDiaryEntryFragment();
- Bundle args = new Bundle();
- args.putLong(ARG_DIARY_ENTRY_ID, diaryEntryId);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- diaryStorage = DiaryStorage.getInstance(requireContext());
- long diaryEntryId = getArguments().getLong(ARG_DIARY_ENTRY_ID);
- diaryEntry = diaryStorage.getDiaryEntryWithId(diaryEntryId);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- titleTextView = view.findViewById(R.id.check_out_fragment_title);
- subtitleTextView = view.findViewById(R.id.check_out_fragment_subtitle);
- doneButton = view.findViewById(R.id.check_out_fragment_done_button);
- cancelButton = view.findViewById(R.id.check_out_fragment_cancel_button);
- fromTime = view.findViewById(R.id.check_out_fragment_from_text_view);
- toTime = view.findViewById(R.id.check_out_fragment_to_text_view);
- dateTextView = view.findViewById(R.id.check_out_fragment_date);
- actionButton = view.findViewById(R.id.check_out_fragment_primary_button);
-
- titleTextView.setText(diaryEntry.getVenueInfo().getTitle());
- subtitleTextView.setText(VenueInfoExtensionsKt.getSubtitle(diaryEntry.getVenueInfo()));
-
- refreshTimeTextViews();
-
- fromTime.setOnClickListener(v -> showTimePicker(true));
- toTime.setOnClickListener(v -> showTimePicker(false));
-
- doneButton.setOnClickListener(v -> {
- saveEntry();
- requireActivity().getSupportFragmentManager().popBackStack();
- });
- cancelButton.setOnClickListener(v -> {
- requireActivity().getSupportFragmentManager().popBackStack();
- });
-
- actionButton.setText(R.string.remove_from_diary_button);
- actionButton.setOnClickListener(v -> hideInDiary());
- }
-
- private void refreshTimeTextViews() {
- fromTime.setText(StringUtil.getHourMinuteTimeString(diaryEntry.getArrivalTime(), " : "));
- toTime.setText(StringUtil.getHourMinuteTimeString(diaryEntry.getDepartureTime(), " : "));
- dateTextView.setText(StringUtil.getCheckOutDateString(getContext(), diaryEntry.getArrivalTime(),
- diaryEntry.getDepartureTime()));
- }
-
- private void showTimePicker(boolean isFromTime) {
- Calendar time = Calendar.getInstance();
- if (isFromTime) {
- time.setTimeInMillis(diaryEntry.getArrivalTime());
- } else {
- time.setTimeInMillis(diaryEntry.getDepartureTime());
- }
- int hour = time.get(Calendar.HOUR_OF_DAY);
- int minute = time.get(Calendar.MINUTE);
- TimePickerDialog timePicker;
- timePicker = new TimePickerDialog(getContext(), (picker, selectedHour, selectedMinute) -> {
- time.set(Calendar.HOUR_OF_DAY, selectedHour);
- time.set(Calendar.MINUTE, selectedMinute);
- if (isFromTime) {
- diaryEntry.setArrivalTime(time.getTimeInMillis());
- } else {
- diaryEntry.setDepartureTime(time.getTimeInMillis());
- }
- if (diaryEntry.getDepartureTime() < diaryEntry.getArrivalTime()) {
- diaryEntry.setDepartureTime(diaryEntry.getDepartureTime() + ONE_DAY_IN_MILLIS);
- } else if (diaryEntry.getArrivalTime() + ONE_DAY_IN_MILLIS < diaryEntry.getDepartureTime()) {
- diaryEntry.setDepartureTime(diaryEntry.getDepartureTime() - ONE_DAY_IN_MILLIS);
- }
- refreshTimeTextViews();
- }, hour, minute, true);
-
- timePicker.show();
- }
-
- private void saveEntry() {
- CrowdNotifier.updateCheckIn(diaryEntry.getId(), diaryEntry.getArrivalTime(), diaryEntry.getDepartureTime(),
- diaryEntry.getVenueInfo(), getContext());
- diaryStorage.updateEntry(diaryEntry);
- }
-
- private void hideInDiary() {
- requireActivity().getSupportFragmentManager().beginTransaction()
- .add(HideInDiaryDialogFragment.newInstance(diaryEntry.getId()), HideInDiaryDialogFragment.TAG)
- .commit();
- }
-
-}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
new file mode 100644
index 000000000..c54e51c53
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
@@ -0,0 +1,72 @@
+package ch.admin.bag.dp3t.checkin.diary
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import ch.admin.bag.dp3t.R
+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.getSubtitle
+import org.crowdnotifier.android.sdk.CrowdNotifier
+
+class EditDiaryEntryFragment : Fragment() {
+
+ companion object {
+ private val TAG = EditDiaryEntryFragment::class.java.canonicalName
+
+ private const val ARG_DIARY_ENTRY_ID = "ARG_DIARY_ENTRY_ID"
+
+ @JvmStatic
+ fun newInstance(diaryEntryId: Long): EditDiaryEntryFragment {
+ val fragment = EditDiaryEntryFragment()
+ val args = Bundle()
+ args.putLong(ARG_DIARY_ENTRY_ID, diaryEntryId)
+ fragment.arguments = args
+ return fragment
+ }
+ }
+
+ private lateinit var diaryStorage: DiaryStorage
+ private lateinit var diaryEntry: DiaryEntry
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ diaryStorage = DiaryStorage.getInstance(requireContext())
+ val diaryEntryId = requireArguments().getLong(ARG_DIARY_ENTRY_ID)
+ diaryEntry = diaryStorage.getDiaryEntryWithId(diaryEntryId)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return FragmentCheckOutAndEditBinding.inflate(inflater, container, false).apply {
+ checkoutTitle.text = diaryEntry.venueInfo.title
+ checkoutSubtitle.setText(diaryEntry.venueInfo.getSubtitle())
+
+ toolbarDoneButton.setOnClickListener { performSave() }
+ toolbarCancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ checkoutPrimaryButton.setText(R.string.remove_from_diary_button)
+ checkoutPrimaryButton.setOnClickListener { hideInDiary() }
+ }.root
+ }
+
+ private fun performSave() {
+ saveEntry()
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+
+ private fun saveEntry() {
+ diaryEntry.run {
+ CrowdNotifier.updateCheckIn(id, arrivalTime, departureTime, venueInfo, context)
+ }
+ diaryStorage.updateEntry(diaryEntry)
+ }
+
+ private fun hideInDiary() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .add(HideInDiaryDialogFragment.newInstance(diaryEntry.id), HideInDiaryDialogFragment.TAG)
+ .commit()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
index bb7709af4..f1072f36d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/models/CheckInState.java
@@ -7,7 +7,7 @@ public class CheckInState {
private VenueInfo venueInfo;
private long checkInTime;
private long checkOutTime;
- long selectedReminderDelay;
+ private long selectedReminderDelay;
public CheckInState(boolean isCheckedIn, VenueInfo venueInfo, long checkInTime, long checkOutTime,
long selectedReminderDelay) {
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 866e8f399..bc833c305 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
@@ -14,7 +14,7 @@
android:paddingHorizontal="@dimen/spacing_medium_large">
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+ android:text="@string/datepicker_to" />
+
+
Date: Tue, 25 May 2021 13:48:05 +0200
Subject: [PATCH 096/207] hide and delete event in diary
---
.../diary/HideInDiaryDialogFragment.java | 21 ++-
.../main/res/drawable/ic_visibility_off.xml | 10 ++
.../layout/dialog_fragment_hide_in_diary.xml | 140 ++++++++++--------
app/src/main/res/values-de/strings.xml | 10 +-
app/src/main/res/values/strings.xml | 6 +
app/src/main/res/values/styles.xml | 4 +
6 files changed, 124 insertions(+), 67 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_visibility_off.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
index 7b8a85d91..fa29f5223 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
@@ -1,5 +1,6 @@
package ch.admin.bag.dp3t.checkin.diary;
+import android.graphics.Paint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -45,21 +46,20 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- View closeButton = view.findViewById(R.id.remove_diary_entry_dialog_close_button);
- View hideButton = view.findViewById(R.id.hide_diary_entry_dialog_hide_button);
- TextView explanationText = view.findViewById(R.id.hide_diary_entry_dialog_text);
+ View closeButton = view.findViewById(R.id.checkin_remove_close_button);
+ View hideButton = view.findViewById(R.id.checkin_remove_hide_button);
+ TextView nukeButton = view.findViewById(R.id.checkin_remove_nuke_button);
+
+ nukeButton.setPaintFlags(nukeButton.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
- String locationInfo = diaryEntry.getVenueInfo().getDescription() + ", " + diaryEntry.getVenueInfo().getAddress();
- explanationText.setText(getString(R.string.remove_diary_warning_text).replace("{LOCATION_INFO}", locationInfo));
closeButton.setOnClickListener(v -> dismiss());
hideButton.setOnClickListener(v -> hideNow());
+ nukeButton.setOnClickListener(v -> nukeNow());
}
@Override
public void onResume() {
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- getDialog().getWindow().setBackgroundDrawableResource(R.drawable.dialog_background);
-
super.onResume();
}
@@ -69,4 +69,11 @@ private void hideNow() {
requireActivity().getSupportFragmentManager().popBackStack();
}
+ private void nukeNow() {
+ // TODO: use correct delete function
+ diaryStorage.removeEntry(diaryEntry.getId());
+ dismiss();
+ requireActivity().getSupportFragmentManager().popBackStack();
+ }
+
}
diff --git a/app/src/main/res/drawable/ic_visibility_off.xml b/app/src/main/res/drawable/ic_visibility_off.xml
new file mode 100644
index 000000000..eb696841d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visibility_off.xml
@@ -0,0 +1,10 @@
+
+
+
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 4042c59d4..ab9aee7d1 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
@@ -1,75 +1,99 @@
-
-
+
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+
+
+ android:layout_marginTop="@dimen/spacing_very_small"
+ android:layout_marginBottom="@dimen/spacing_medium_large"
+ android:gravity="start"
+ android:paddingHorizontal="@dimen/spacing_larger"
+ android:text="@string/remove_diary_warning_title" />
-
+
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/spacing_larger"
+ android:paddingVertical="@dimen/spacing_large">
-
+ android:layout_marginBottom="@dimen/spacing_medium_large"
+ android:text="@string/remove_diary_warning_hide_title" />
-
+
-
+
-
+
-
+
+
+
-
+
-
-
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1cc226dbb..b9db76ed5 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1723,13 +1723,13 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Don't share"
- "SwissCovid Check-Ins"
+ "SwissCovid-Check-in""Einchecken ohne Daten zu hinterlassen"
- "Mit der SwissCovid App können Sie sich bei uns einfach einchecken ohne Ihre Daten zu hinterlassen. Bei einer Risikosituation werden Sie in der App schnell informiert."
+ "Mit der SwissCovid App können Sie sich bei uns einfach einchecken, ohne Ihre Daten zu hinterlassen. Bei einer Risikosituation werden Sie in der App schnell informiert.""SwissCovid App"
@@ -1855,4 +1855,10 @@ Dieser Titel wird beim Check-in angezeigt."
"Was muss ich übers Testen wissen?"
+ "App installieren und einchecken"
+ "Schliessen Sie die App-Konfiguration ab, um die Tracing-Funktion verwenden zu können."
+ "Jetzt abschliessen"
+ "Konfiguration abgeschlossen"
+ "Vielen Dank, dass Sie mithelfen, die Verbreitung des Coronavirus einzudämmen."
+ "Bei einem Ansteckungsrisiko schnell informiert."
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ba0b5a3f2..31b3899ed 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1793,4 +1793,10 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 47da8f4e6..47c8ab8ff 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -134,6 +134,10 @@
@color/blue_main
+
+
From 13d761e20733e20aab721f3259ede15dbdd3abbd Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Mon, 24 May 2021 23:57:51 +0200
Subject: [PATCH 097/207] highlight notifications in diary
---
.../java/ch/admin/bag/dp3t/checkin/diary/DiaryFragment.kt | 6 +++---
app/src/main/res/layout/item_checkin_venue_visit.xml | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
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 616a158e3..d46ff2373 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
@@ -40,15 +40,15 @@ class DiaryFragment : Fragment() {
checkinDiaryRecyclerView.adapter = recyclerAdapter
crowdNotifierViewModel.exposures.observe(viewLifecycleOwner) { exposures ->
- val items: ArrayList = ArrayList()
- val diaryEntries: List = DiaryStorage.getInstance(context).entries.sortedByDescending {
+ val items = ArrayList()
+ val diaryEntries = DiaryStorage.getInstance(context).entries.sortedByDescending {
it.arrivalTime
}
val isEmpty = diaryEntries.isEmpty()
checkinDiaryEmptyView.isVisible = isEmpty
var daysAgoString = ""
for (diaryEntry in diaryEntries) {
- val newDaysAgoString: String = StringUtil.getDaysAgoString(diaryEntry.getArrivalTime(), context)
+ val newDaysAgoString: String = StringUtil.getDaysAgoString(diaryEntry.arrivalTime, context)
if (newDaysAgoString != daysAgoString) {
daysAgoString = newDaysAgoString
items.add(ItemVenueVisitDayHeader(daysAgoString))
diff --git a/app/src/main/res/layout/item_checkin_venue_visit.xml b/app/src/main/res/layout/item_checkin_venue_visit.xml
index 29585483b..6c1304510 100644
--- a/app/src/main/res/layout/item_checkin_venue_visit.xml
+++ b/app/src/main/res/layout/item_checkin_venue_visit.xml
@@ -5,7 +5,7 @@
style="@style/CrowdNotifier.Card.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginVertical="@dimen/spacing_tiny_small">
+ android:layout_marginVertical="@dimen/spacing_very_small">
From 248f6b1e0bffea726520350f99ae8c485eaf2e0f Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 25 May 2021 00:01:52 +0200
Subject: [PATCH 098/207] fixed 'x days ago' logic
---
.../java/ch/admin/bag/dp3t/util/StringUtil.java | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
index 89d2946c5..95c0cf8d6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/StringUtil.java
@@ -121,17 +121,14 @@ public static String getCheckOutDateString(Context context, long checkInTime, lo
}
public static String getDaysAgoString(long timeStamp, Context context) {
- final long diff = DateUtils.getDaysDiff(timeStamp);
- if (diff < 0) {
+ final long daysAgo = DateUtils.getDaysDiff(timeStamp);
+ if (daysAgo <= 0) {
return context.getResources().getString(R.string.date_today);
+ } else if (daysAgo == 1) {
+ return context.getResources().getString(R.string.date_one_day_ago);
} else {
- long daysAgo = TimeUnit.MILLISECONDS.toDays(diff) + 1;
- if (daysAgo == 1) {
- return context.getResources().getString(R.string.date_one_day_ago);
- } else {
- return context.getResources().getString(R.string.date_days_ago)
- .replace("{COUNT}", String.valueOf(daysAgo));
- }
+ return context.getResources().getString(R.string.date_days_ago)
+ .replace("{COUNT}", String.valueOf(daysAgo));
}
}
From e66c96bfd28b93f429ec64557a00ce7441675007 Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 25 May 2021 14:51:42 +0200
Subject: [PATCH 099/207] delete checkin for good
---
app/build.gradle | 3 +--
.../bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java | 4 +++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c3302c1b5..e224a8ca4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -207,8 +207,7 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-9'
-
+ implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-10'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
index fa29f5223..0c2b2f15c 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/HideInDiaryDialogFragment.java
@@ -10,6 +10,8 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
+import org.crowdnotifier.android.sdk.CrowdNotifier;
+
import ch.admin.bag.dp3t.R;
import ch.admin.bag.dp3t.checkin.models.DiaryEntry;
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
@@ -70,8 +72,8 @@ private void hideNow() {
}
private void nukeNow() {
- // TODO: use correct delete function
diaryStorage.removeEntry(diaryEntry.getId());
+ CrowdNotifier.deleteCheckIn(diaryEntry.getId(), requireContext());
dismiss();
requireActivity().getSupportFragmentManager().popBackStack();
}
From b285aba16f226e82412728723e80c0637ef40e5e Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 25 May 2021 15:29:47 +0200
Subject: [PATCH 100/207] Update Report Screen UI
---
.../admin/bag/dp3t/reports/ReportsFragment.kt | 62 +------
.../layout/card_what_to_do_checkin_report.xml | 97 ++--------
.../res/layout/card_what_to_do_leitfaden.xml | 172 +-----------------
.../main/res/layout/view_report_details.xml | 127 +++++++++++++
app/src/main/res/values-bs/strings.xml | 14 +-
app/src/main/res/values-de/strings.xml | 70 ++++---
app/src/main/res/values-es/strings.xml | 14 +-
app/src/main/res/values-fr/strings.xml | 14 +-
app/src/main/res/values-hr/strings.xml | 14 +-
app/src/main/res/values-it/strings.xml | 14 +-
app/src/main/res/values-pt/strings.xml | 14 +-
app/src/main/res/values-rm/strings.xml | 14 +-
app/src/main/res/values-sq/strings.xml | 14 +-
app/src/main/res/values-sr/strings.xml | 14 +-
app/src/main/res/values-ti/strings.xml | 12 +-
app/src/main/res/values-tr/strings.xml | 14 +-
app/src/main/res/values/strings.xml | 14 +-
17 files changed, 261 insertions(+), 433 deletions(-)
create mode 100644 app/src/main/res/layout/view_report_details.xml
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
index 0e22730d5..1b5fbdffa 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
@@ -45,16 +45,13 @@ import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.util.*
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import ch.admin.bag.dp3t.whattodo.WhereToTestDialogFragment
-import org.dpppt.android.sdk.models.DayDate
import java.util.*
import kotlin.math.min
-private const val DAYS_TO_STAY_IN_QUARANTINE = 10
-private const val MAX_EXPOSURE_AGE_TO_DO_A_TEST = 10
-private const val MIN_EXPOSURE_AGE_TO_DO_A_TEST = 5
-private const val ONE_DAY_IN_MILLIS = 24L * 60 * 60 * 1000
private const val ARG_CHECK_IN_ID = "ARG_CHECK_IN_ID"
private const val ARG_SHOW_TRACING_REPORT_DETAILS = "ARG_SHOW_TRACING_REPORT_DETAILS"
+private const val TESTING_EXTERNAL_LINK =
+ "https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
class ReportsFragment : Fragment() {
@@ -203,13 +200,12 @@ class ReportsFragment : Fragment() {
}
State.POSSIBLE_INFECTION_TRACING_REPORTS -> {
binding.reportsLeitfaden.apply {
- itemCallHotlineLayout.setOnClickListener { callHotline() }
+ reportFurtherInformation.itemCallHotlineLayout.setOnClickListener { callHotline() }
+ reportFurtherInformation.testsExternalLink.setOnClickListener { showTestInformation() }
faqButton.setOnClickListener { showFaq() }
val isOpenLeitfadenPending = secureStorage.isOpenLeitfadenPending
fillLeitfadenNowButton.isVisible = isOpenLeitfadenPending
zumLeitfadenButton.isVisible = !isOpenLeitfadenPending
- xDaysLeftTextview.isVisible = !isOpenLeitfadenPending
- setupFreeTestInfoBox(tracingStatusInterface)
if (isOpenLeitfadenPending) {
fillLeitfadenNowButton.setOnClickListener { openSwissCovidLeitfaden() }
leitfadenInfoButton.setOnClickListener { showLeitfadenInfo(fillLeitfadenNowButton.text.toString()) }
@@ -217,15 +213,6 @@ class ReportsFragment : Fragment() {
zumLeitfadenButton.setOnClickListener { openSwissCovidLeitfaden() }
leitfadenInfoButton.setOnClickListener { showLeitfadenInfo(zumLeitfadenButton.text.toString()) }
}
-
- val daysLeft = DAYS_TO_STAY_IN_QUARANTINE - tracingStatusInterface.daysSinceExposure.toInt()
- if (daysLeft > DAYS_TO_STAY_IN_QUARANTINE || daysLeft <= 0) {
- xDaysLeftTextview.isVisible = false
- } else if (daysLeft == 1) {
- xDaysLeftTextview.setText(R.string.date_in_one_day)
- } else {
- xDaysLeftTextview.text = getString(R.string.date_in_days).replace("{COUNT}", daysLeft.toString())
- }
deleteReports.setOnClickListener { deleteNotifications(tracingStatusInterface) }
}
}
@@ -256,7 +243,8 @@ class ReportsFragment : Fragment() {
}
faqButton.setOnClickListener { showFaq() }
- testlocationsLink.setOnClickListener { showWhereToTestDialog() }
+ reportFurtherInformation.testsExternalLink.setOnClickListener { showTestInformation() }
+ reportFurtherInformation.phoneSection.isVisible = false
}
}
}
@@ -295,40 +283,6 @@ class ReportsFragment : Fragment() {
.show()
}
- private fun setupFreeTestInfoBox(tracingStatusInterface: TracingStatusInterface) {
- binding.reportsLeitfaden.viewFreeTestInfoBox.apply {
- testlocationsLink.setOnClickListener { showWhereToTestDialog() }
- val today = DayDate()
- if (tracingStatusInterface.exposureDays.filter {
- it.exposedDate.addDays(MIN_EXPOSURE_AGE_TO_DO_A_TEST).isBeforeOrEquals(today)
- }.find {
- !it.exposedDate.addDays(MAX_EXPOSURE_AGE_TO_DO_A_TEST).isBefore(today)
- } != null) {
- testCountdownTextview.setText(R.string.meldungen_detail_free_test_now)
- return
- }
-
- // This oldest exposure that is newer than MIN_EXPOSURE_AGE_TO_DO_A_TEST
- val oldestExposure = tracingStatusInterface.exposureDays.filter {
- !it.exposedDate.addDays(MIN_EXPOSURE_AGE_TO_DO_A_TEST).isBeforeOrEquals(today)
- }.minOf { it.exposedDate }
-
- testCountdownTextview.isVisible = oldestExposure != null
-
- if (oldestExposure != null) {
- val daysSinceFirstExposure =
- ((today.startOfDayTimestamp - oldestExposure.startOfDayTimestamp) / ONE_DAY_IN_MILLIS).toInt()
- val daysUntilTest = MIN_EXPOSURE_AGE_TO_DO_A_TEST - daysSinceFirstExposure
- if (daysUntilTest == 1) {
- testCountdownTextview.setText(R.string.meldungen_detail_free_test_tomorrow)
- } else {
- testCountdownTextview.text = getString(R.string.meldungen_detail_free_test_in_x_tagen)
- .replace("{COUNT}", daysUntilTest.toString())
- }
- }
- }
- }
-
private fun showWhereToTestDialog() {
requireActivity().supportFragmentManager.beginTransaction()
.add(WhereToTestDialogFragment.newInstance(), WhereToTestDialogFragment::class.java.canonicalName)
@@ -365,6 +319,10 @@ class ReportsFragment : Fragment() {
).commit()
}
+ private fun showTestInformation() {
+ UrlUtil.openUrl(context, TESTING_EXTERNAL_LINK)
+ }
+
private fun callHotline() {
PhoneUtil.callHelpline(context)
}
diff --git a/app/src/main/res/layout/card_what_to_do_checkin_report.xml b/app/src/main/res/layout/card_what_to_do_checkin_report.xml
index 69e14550b..ce890d778 100644
--- a/app/src/main/res/layout/card_what_to_do_checkin_report.xml
+++ b/app/src/main/res/layout/card_what_to_do_checkin_report.xml
@@ -20,7 +20,7 @@
style="@style/NextStep.Text.Bold.Blue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/spacing_large"
+ android:layout_marginHorizontal="@dimen/spacing_larger"
android:layout_marginTop="@dimen/spacing_large"
android:text="@string/meldung_detail_exposed_list_card_subtitle" />
@@ -28,7 +28,7 @@
style="@style/NextStep.Text.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/spacing_large"
+ android:layout_marginHorizontal="@dimen/spacing_larger"
android:layout_marginTop="@dimen/spacing_very_small"
android:text="@string/meldung_detail_exposed_list_card_title_checkin" />
@@ -37,7 +37,7 @@
style="@style/NextStep.Text.Bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/spacing_large"
+ android:layout_marginHorizontal="@dimen/spacing_larger"
android:lineSpacingExtra="@dimen/spacing_very_small"
tools:text="20.10.2020 / vor 2 Tagen \n22.10.2020 / vor 4 Tagen" />
@@ -46,7 +46,7 @@
style="@style/NextStep.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/spacing_large"
+ android:layout_marginHorizontal="@dimen/spacing_larger"
android:layout_marginTop="@dimen/spacing_very_small"
tools:text="Reuss Ubique" />
@@ -60,90 +60,19 @@
style="@style/NextStep.Text.Bold.Blue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/spacing_large"
+ android:layout_marginHorizontal="@dimen/spacing_larger"
android:layout_marginTop="@dimen/spacing_large"
+ android:layout_marginBottom="@dimen/spacing_small"
android:text="@string/checkin_report_heading" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ android:textColor="@color/blue_main" />
diff --git a/app/src/main/res/layout/card_what_to_do_leitfaden.xml b/app/src/main/res/layout/card_what_to_do_leitfaden.xml
index 8c71e9e11..2819461fa 100644
--- a/app/src/main/res/layout/card_what_to_do_leitfaden.xml
+++ b/app/src/main/res/layout/card_what_to_do_leitfaden.xml
@@ -9,7 +9,6 @@
-->
@@ -32,8 +31,7 @@
android:clipToPadding="false"
android:foreground="?selectableItemBackground"
android:orientation="vertical"
- android:paddingLeft="@dimen/spacing_larger"
- android:paddingRight="@dimen/spacing_larger">
+ android:paddingHorizontal="@dimen/spacing_larger">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_marginTop="@dimen/spacing_larger" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index 41031419f..4c661df6b 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -589,14 +589,6 @@ Time se potrošnja baterije samo neznatno povećava."
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
-
- "Šta je moguća infekcija?"
-
-
- "Aplikacija ne samo da proverava da li ste imali kontakte sa inficiranim osobama, već i koliko dugo su oni trajali i koliko bliski su bili.
-
-Bićete obavešteni samo ako postoji dovoljno velika mogućnost infekcije."
-
"Preko info linije aplikacije SwissCovid možete da dobijete savete o mogućnosti besplatnog testiranja."
@@ -1770,4 +1762,10 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1cc226dbb..6ae4055ef 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -194,7 +194,7 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Mit dem Covidcode..."
- "Infektionsketten stoppen"
+ "Vor möglichen Ansteckungen warnen""Covidcode eingeben"
@@ -227,8 +227,8 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Symptome""Tracing deaktiviert""Aktivieren Sie das Tracing, damit die App funktioniert."
- "Bluetooth Off"
- "Schalten Sie Bluetooth auf Ihrem Mobiltelefon ein, damit das Tracing funktioniert."
+ "Bluetooth aus"
+ "Schalten Sie Bluetooth auf Ihrem Gerät ein, damit das anonyme Tracing funktioniert.""Bluetooth einschalten""Bluetooth Verwendung abgelehnt""Erlauben Sie die Verwendung von Bluetooth, damit das Tracing funktioniert."
@@ -260,7 +260,7 @@ Damit helfen Sie mit, Infektionsketten zu stoppen."
"Befolgen Sie die erhaltenen Anweisungen zur Isolation"
- "Tracing deaktiviert"
+ "Begegnungen und Check-in deaktiviert""Es werden keine Begegnungen gespeichert""Kontaktieren Sie die""Befolgen Sie die"
@@ -597,18 +597,10 @@ Die Batterienutzung erhöht sich dadurch nur leicht."
"Was sind Meldungen?"
- "Die App prüft regelmässig, ob andere Nutzer, denen Sie begegnet sind, einen positiven Test gemeldet haben.
+ "Die App prüft regelmässig, ob andere Nutzer, denen Sie begegnet sind oder die am gleichen Ort eingecheckt waren, ein positives Testergebnis gemeldet haben.
Bei einer möglichen Ansteckung werden Sie mit einer Mitteilung im Sperrbildschirm informiert."
-
- "Was bedeutet mögliche Ansteckung?"
-
-
- "Die App prüft nicht nur, ob Begegnungen mit infizierten Personen stattfanden, sondern auch wie lange und wie nahe diese waren.
-
-Nur wenn die Möglichkeit einer Ansteckung gross genug ist, werden Sie informiert."
-
"Die Infoline SwissCovid berät Sie über die Möglichkeit eines kostenlosen Tests."
@@ -647,9 +639,9 @@ Damit wird sichergestellt, dass nur bestätigte Fälle über die App gemeldet we
"Was wird gesendet?"
- "Es werden nur die privaten Schlüssel Ihrer App gesendet, keine persönlichen Daten.
+ "Es werden nur die privaten Schlüssel Ihrer App und die verschlüsselten IDs Ihrer Check-ins gesendet, keine persönlichen Daten.
-Damit erfahren andere SwissCovid App Nutzer, dass die Möglichkeit einer Ansteckung besteht."
+Damit erfahren andere Nutzerinnen und Nutzer der SwissCovid-App, dass die Möglichkeit einer Ansteckung besteht.""+41 58 387 77 78"
@@ -661,7 +653,7 @@ Damit erfahren andere SwissCovid App Nutzer, dass die Möglichkeit einer Ansteck
"Information zur Eingabe"
- "Obwohl keine Daten zu Ihrer Person gesendet werden, kann es sein, dass sich jemand anhand des Datums an die Begegnung erinnern kann."
+ "Obwohl keine Daten zu Ihrer Person gesendet werden, könnte es sein, dass sich jemand anhand des Datums an die Begegnung mit Ihnen erinnern kann.""Einverstanden"
@@ -772,7 +764,7 @@ Zudem sind folgende Symptome möglich:
"Möchten Sie die Isolation wirklich beenden?""Akku-Optimierung ignorieren"
- "Damit die App funktioniert, muss die Akku-Optimierung für diese App ausgeschaltet sein."
+ "Damit die App funktioniert, muss die Akku-Optimierung ausgeschaltet sein.""Zulassen""OK""Nur für diese App"
@@ -796,7 +788,7 @@ Zudem sind folgende Symptome möglich:
- "Aktivieren Sie {TRACING_SETTING_TEXT}, damit das Tracing funktioniert."
+ "Aktivieren Sie {TRACING_SETTING_TEXT}, damit das anonyme Tracing funktioniert.""Die Meldung konnte nicht gesendet werden. Bitte erlauben Sie das Teilen Ihrer zufälligen IDs mit der SwissCovid App."
@@ -842,7 +834,7 @@ Falls auf Ihrem Mobiltelefon zurzeit kein Update verfügbar ist, versuchen Sie e
"SwissCovid –
bei einem Ansteckungsrisiko schnell informiert."
- "Obwohl keine Daten zu Ihrer Person gesendet werden, kann es sein, dass sich jemand anhand des Datums an die Begegnung erinnern kann."
+ "Ja, aber obwohl keine Daten zu Ihrer Person gesendet werden, könnte es sein, dass sich jemand anhand des Datums an die Begegnung mit Ihnen erinnern kann.""Bleibe ich anonym?"
@@ -946,7 +938,7 @@ Schwarzenburgstrasse 157
"Reisen"
- "SwissCovid funktioniert in den folgenden Ländern:"
+ "SwissCovid-Begegnungen funktioniert in den folgenden Ländern:"
@@ -1297,9 +1289,9 @@ Damit auch Reisende aus dem Ausland gewarnt werden können, geschieht das unabh
"Beenden"
- "Es werden keine neuen Begegnungen mehr gespeichert und neue Check-ins sind derzeit nicht möglich.
+ "Es werden keine neuen Begegnungen mehr gespeichert und neue Check-ins sind auch nicht mehr möglich.
-Beide Funktionen können nach der Isolation wieder aktiviert werden."
+Beide Funktionen können wieder aktiviert werden, nachdem die Isolation beendet wurde.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1332,7 +1324,7 @@ Beide Funktionen können nach der Isolation wieder aktiviert werden."
- "Sind Sie gehörlos oder hörbehindert und können die Infoline Coronavirus nicht anrufen?
+ "Sind Sie gehörlos oder hörbehindert und können nicht anrufen?
Dann senden Sie eine E-Mail an covid-support@medgate.ch"
@@ -1388,7 +1380,7 @@ Die Zeit ist bei der Bekämpfung des Coronavirus ein entscheidender Faktor: je k
"Sie wurden positiv getestet (PCR-Test oder Antigen-Schnelltest) und haben nach 4h noch keinen Covidcode erhalten?
-Dann kontaktieren Sie die Infoline Coronavirus:"
+Dann rufen Sie hier an:""Noch keinen Covidcode?"
@@ -1413,7 +1405,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Kompatible Länder:"
- "Damit auch Reisende aus dem Ausland gewarnt werden können, werden die privaten Schlüssel mit den Corona-Apps der kompatiblen Länder geteilt."
+ "Damit auch Reisende aus dem Ausland nach einer Begegnung gewarnt werden können, werden die privaten Schlüssel mit den Corona-Apps der kompatiblen Länder geteilt.""Total"
@@ -1428,13 +1420,13 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Wer wird benachrichtigt?"
- "Es werden alle Kontakte im Zeitraum Ihrer Ansteckungsphase vom {ONSET_DATE} bis zur Eingabe des Covidcodes benachrichtigt, wenn auf Grund der Distanz und der Dauer die Möglichkeit einer Ansteckung bestand. "
+ "Es werden alle Kontakte im Zeitraum Ihrer Ansteckungsphase vom {ONSET_DATE} bis zur Eingabe des Covidcodes benachrichtigt, wenn das Risiko einer Ansteckung bestand – auf Grund der Distanz und Dauer oder wenn Sie am gleichen Ort eingecheckt waren.""{ONSET_DATE} – heute"
- "Auf diese Weise helfen Sie, Infektionsketten zu durchbrechen."
+ "Auf diese Weise helfen Sie, andere vor einer möglichen Ansteckung zu warnen.""Die privaten Schlüssel Ihrer App wurden für den folgenden Zeitraum gesendet:"
@@ -1600,9 +1592,9 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"QR-Code noch nicht gültig""QR-Code nicht mehr gültig""Sie sind bereits eingecheckt."
- "Dieser QR Code ist nicht kompatibel mit der installierten App Version. Führen Sie ein Update der App durch, um den QR Code zu scannen."
- "Update benötigt"
- "Jetzt updaten"
+ "Dieser QR-Code ist nicht mit der installierten App-Version kompatibel. Bitte aktualisieren Sie die App, um fortzufahren."
+ "Update erforderlich"
+ "Jetzt aktualisieren""Check out""Snooze""Permanente Checkin Meldung"
@@ -1656,7 +1648,7 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Wenn Sie Ihre Check-in-Einträge teilen, kann die App Personen benachrichtigen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben."
- "Alle wählen"
+ "Alle auswählen""Nicht senden""Erneut versuchen""Nicht teilen"
@@ -1723,13 +1715,13 @@ Dann kontaktieren Sie die Infoline Coronavirus:"
"Don't share"
- "SwissCovid Check-Ins"
+ "SwissCovid-Check-in""Einchecken ohne Daten zu hinterlassen"
- "Mit der SwissCovid App können Sie sich bei uns einfach einchecken ohne Ihre Daten zu hinterlassen. Bei einer Risikosituation werden Sie in der App schnell informiert."
+ "Mit der SwissCovid App können Sie sich bei uns einfach einchecken, ohne Ihre Daten zu hinterlassen. Bei einer Risikosituation werden Sie in der App schnell informiert.""SwissCovid App"
@@ -1754,7 +1746,7 @@ zu hinterlassen"
"Warum eine Check-in-Funktion?"
- "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Sie berücksichtigt nicht nur enge Kontakte, sondern auch solche, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben – unabhängig vom Abstand."
+ "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Mit dieser Funktion können Sie im Falle eines positiven Tests nicht nur enge Kontakte, sondern auch Personen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben, warnen.""Keine zentrale Datenspeicherung"
@@ -1773,7 +1765,7 @@ Mögliche Einsatzorte:
– Private Anlässe
– Vereinsanlässe
– Zusammenkünfte im professionellen Umfeld wie z.B. Sitzungszimmer, Büroräume, Kantinen, etc."
- "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Sie berücksichtigt nicht nur enge Kontakte, sondern auch solche, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben – unabhängig vom Abstand. "
+ "Die Check-in-Funktion ohne Erfassung der Kontaktdaten erweitert die SwissCovid App. Mit dieser Funktion können Sie im Falle eines positiven Tests nicht nur enge Kontakte, sondern auch Personen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben, warnen.""Risikosituation"
@@ -1836,7 +1828,7 @@ Dieser Titel wird beim Check-in angezeigt."
"Check-ins""Mit SwissCovid-Check-ins können Sie sich mit der App an einem Ort oder bei einem Treffen einchecken.""Ihre Anwesenheit wird nur in der App gespeichert. Sie müssen keine persönlichen Daten in der App hinterlassen."
- "Mit SwissCovid-Check-ins können Sie sich mit der App vor Ort einchecken. Ihre Anwesenheit wird nur in der App gespeichert. Sie müssen keine persönlichen Daten in der App hinterlassen. Dazu wurden die Nutzungsbedingungen und Datenschutzerklärung aktualisiert."
+ "Mit SwissCovid-Check-ins können Sie sich mit der App an einem Ort oder bei einem Treffen einchecken. Ihre Anwesenheit wird nur in der App gespeichert. Sie müssen keine persönlichen Daten in der App hinterlassen. Dazu wurden die Nutzungsbedingungen und Datenschutzerklärung aktualisiert.""Überspringen"
@@ -1855,4 +1847,10 @@ Dieser Titel wird beim Check-in angezeigt."
"Was muss ich übers Testen wissen?"
+ "App installieren und einchecken"
+ "Schliessen Sie die App-Konfiguration ab, um die Tracing-Funktion verwenden zu können."
+ "Jetzt abschliessen"
+ "Konfiguration abgeschlossen"
+ "Vielen Dank, dass Sie mithelfen, die Verbreitung des Coronavirus einzudämmen."
+ "Bei einem Ansteckungsrisiko schnell informiert."
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0bfb0c2a4..3bddafd99 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -582,14 +582,6 @@ Siga las instrucciones de aislamiento recibidas."
En caso de un posible contacto, será informado con un mensaje en la pantalla de bloqueo."
-
- "¿Qué es un posible contagio?"
-
-
- "La aplicación no solo detecta si ha habido contacto con una persona contagiada, sino también a qué distancia y durante cuánto tiempo.
-
-La aplicación le informará solo si la posibilidad de un contagio es lo suficientemente grande."
-
"La Infoline SwissCovid le asesora sobre la posibilidad de realizar un test gratuito."
@@ -1761,4 +1753,10 @@ Contacte con la Infoline del coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 4b57fddd2..0f5c72a0b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -587,14 +587,6 @@ Ces identifiants sont enregistrés localement dans les appareils et effacés apr
En cas de possible infection, vous serez informé par message sur l'écran de verrouillage."
-
- "Que signifie possible infection ?"
-
-
- "L'application ne détecte pas seulement si des contacts avec des personnes infectées ont eu lieu, mais aussi la durée et la proximité de ces contacts.
-
-Vous ne serez informé que si le risque d'infection est suffisamment important."
-
"L'infoline SwissCovid vous conseille sur la possibilité d'effectuer un test gratuit."
@@ -1796,4 +1788,10 @@ Contactez l'infoline coronavirus :"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 00458a0ff..41c864c31 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -588,14 +588,6 @@ Time se potrošnja baterije samo neznatno povećava."
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
-
- "Šta je moguća infekcija?"
-
-
- "Aplikacija ne samo da proverava da li ste imali kontakte sa inficiranim osobama, već i koliko dugo su oni trajali i koliko bliski su bili.
-
-Bićete obavešteni samo ako postoji dovoljno velika mogućnost infekcije."
-
"Preko info linije aplikacije SwissCovid možete da dobijete savete o mogućnosti besplatnog testiranja."
@@ -1766,4 +1758,10 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 8d2c5c9cb..7c27d5c1e 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -592,14 +592,6 @@ Il consumo della batteria aumenta soltanto leggermente."
In caso di possibile contagio viene visualizzato un messaggio sulla schermata di blocco."
-
- "Cosa significa possibile contagio?"
-
-
- "L'app non controlla solo se ci sono stati incontri con persone infette, ma anche a quale distanza e per quanto tempo.
-
-L'utente viene informato soltanto se la possibilità di un contagio è sufficientemente fondata."
-
"La Infoline SwissCovid ti offre consulenza sulla possibilità di fare un test gratuito."
@@ -1804,4 +1796,10 @@ Contatta la Infoline coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index a07b8e182..6ee0bfebe 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -590,14 +590,6 @@ Isto aumenta muito pouco o consumo da bateria."
Em caso de possível contágio, receberá uma notificação no ecrã de bloqueio."
-
- "O que significa um possível contágio?"
-
-
- "A app não só verifica se houve contactos com pessoas infetadas, mas também quanto tempo e quão perto esteve dessas pessoas.
-
-Só será notificado se houver uma probabilidade razoável de contágio."
-
"A linha informativa SwissCovid aconselha-o sobre um possível teste gratuito."
@@ -1771,4 +1763,10 @@ Contacte a linha de informação coronavírus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 2ca22c683..9b116c6e1 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -586,14 +586,6 @@ ll diever da la battaria s'augmenta qua tras mo levamain."
En cas d'ina eventuala infecziun vegnis Vus orientada/orientà cun ina communicaziun sin il visur bloccà."
-
- "Tge vul dir eventuala infecziun?"
-
-
- "L'app n'examinescha betg sulettamain, sch'i ha dà inscunters cun persunas infectadas, mabain er quant ditg ch'els han durà e quant damanaivel che Vus essas stada/stà a questas persunas.
-
-Vus vegnis infurmada/infurmà mo, sche la pussaivladad d'esser infectada/infectà è gronda avunda."
-
"L'infoline SwissCovid As cusseglia davart la pussaivladad d'in test gratuit."
@@ -1766,4 +1758,10 @@ Alura contactai la Infoline coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index f422d6820..4af0a8abe 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -589,14 +589,6 @@ Nga kjo, konsumi i baterisë rritet vetëm pak."
Në rast infeksioni të mundshëm informoheni me një mesazh në ekranin e mbyllur."
-
- "Çfarë do të thotë infeksion i mundshëm?"
-
-
- "Aplikacioni nuk kontrollon vetëm nëse ka pasur kontakt me persona të infektuar, por edhe se për sa kohë dhe në çfarë distance ishin këta persona.
-
-Përdoruesi informohet vetëm nëse dyshimi për një infeksion të mundshëm është mjaft i madh."
-
"Infoline SwissCovid ju këshillon për mundësinë e një testi falas."
@@ -1771,4 +1763,10 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 09df5bb84..112e8d072 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -588,14 +588,6 @@ Time se potrošnja baterije samo neznatno povećava."
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
-
- "Šta je moguća infekcija?"
-
-
- "Aplikacija ne samo da proverava da li ste imali kontakte sa inficiranim osobama, već i koliko dugo su oni trajali i koliko bliski su bili.
-
-Bićete obavešteni samo ako postoji dovoljno velika mogućnost infekcije."
-
"Preko info linije aplikacije SwissCovid možete da dobijete savete o mogućnosti besplatnog testiranja."
@@ -1766,4 +1758,10 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml
index 01316aaea..561dbb8bf 100644
--- a/app/src/main/res/values-ti/strings.xml
+++ b/app/src/main/res/values-ti/strings.xml
@@ -576,12 +576,6 @@
"እቲ ኣፕ ክልኦት ዝተራኸብኩሞም ተጠቀምቲ ብመርመራ ፖዚቲቭ ከምዝኾኑ ሓቢሮም እንተኾይኖም ቀጻሊ ይምርምር።
ተኽእሎ ምልካፍ ብሕማም ምስዝህሉ ብመነግዲ መልእኽቲ ኣብ ዕጹው ስክሪን ሞባይል ክትሕበሩ ኢኹም። "
-
- "ተኽእሎ ምልካፍ ብሕማም እንታይ ማለት እዩ፧"
-
-
- "እቲ ኣፕ ርክብ ምስ ዝተለኸፉ ሰባት ኣጓኒፉ እንተኾይኑ ጥራይ ዘይኮነስ ንውሒ ግዜን ዓቐን ናይቲ ቅርበትን እዩ ዝምርምር። ቡቑዕ ተኽእሎ ናይ መልከፍቲ ምስዝህሉ ጥራይ ኢኹም ትሕበሩ።"
-
"መስመር ሓበሬታ ስዊስኮቪድ ብዛዕባ ተኽእሎ ካብ ክፍሊት ነጻ ናይ ዝኾነ መርመራ የማኽረኩም። "
@@ -1746,4 +1740,10 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 72797e67e..deb5dcc17 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -576,14 +576,6 @@ Bunlar cep telefonunda kaydedilmekte ve 14 gün sonra silinmektedir."
"Uygulama düzenli olarak temas ettiğiniz kişilerin pozitif bir test sonucu bildirip bildirmediğini kontrol eder.
Olası bir enfeksiyon riskinde kilit ekranında bir mesaj ile bilgilendirileceksiniz."
-
- "Enfeksiyon riski ne anlama gelmektedir?"
-
-
- "Uygulama yalnızca enfekte kişiler temaz olup olmadığını değil, temasların ne kadar sürdüğünü ve ne kadar yakın olduklarını da takip eder.
-
-Yalnızca bulaşma riski yeterince büyük ise bilgilendirilirsiniz."
-
"SwissCovid Bilgi Hattı sizi ücretsiz testler konusunda bilgilendirir."
@@ -1748,4 +1740,10 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ba0b5a3f2..18eaafde7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -588,14 +588,6 @@ They are saved locally on the smartphones and then deleted 14 days later."
-
- "What is a potential infection?"
-
-
- "The app not only checks whether you have had encounters with infected persons, but how long and how close these were.
-
-You will only be notified if there is sufficient likelihood that you have been infected."
-
"The infoline SwissCovid will advise you on getting a free test."
@@ -1793,4 +1785,10 @@ If so, contact the Coronavirus Infoline:"
+
+
+
+
+
+
\ No newline at end of file
From 0f81b4fca7fba64f99fe19040e953af56f8c7ae6 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 25 May 2021 15:33:47 +0200
Subject: [PATCH 101/207] Update Icons
---
app/src/main/res/drawable/ic_location_pin.xml | 10 ++++++++++
app/src/main/res/drawable/ic_smartphone.xml | 10 ++++++++++
app/src/main/res/drawable/ic_stopwatch.xml | 10 ++++++++++
app/src/main/res/layout/fragment_checkin_overview.xml | 6 ++----
app/src/main/res/layout/item_events_footer.xml | 6 ++----
5 files changed, 34 insertions(+), 8 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_location_pin.xml
create mode 100644 app/src/main/res/drawable/ic_smartphone.xml
create mode 100644 app/src/main/res/drawable/ic_stopwatch.xml
diff --git a/app/src/main/res/drawable/ic_location_pin.xml b/app/src/main/res/drawable/ic_location_pin.xml
new file mode 100644
index 000000000..5069074de
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location_pin.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_smartphone.xml b/app/src/main/res/drawable/ic_smartphone.xml
new file mode 100644
index 000000000..576481770
--- /dev/null
+++ b/app/src/main/res/drawable/ic_smartphone.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_stopwatch.xml b/app/src/main/res/drawable/ic_stopwatch.xml
new file mode 100644
index 000000000..893353f26
--- /dev/null
+++ b/app/src/main/res/drawable/ic_stopwatch.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_checkin_overview.xml b/app/src/main/res/layout/fragment_checkin_overview.xml
index 7f44d4506..91675ff6d 100644
--- a/app/src/main/res/layout/fragment_checkin_overview.xml
+++ b/app/src/main/res/layout/fragment_checkin_overview.xml
@@ -293,8 +293,7 @@
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginRight="@dimen/spacing_medium_large"
- android:src="@drawable/ic_shield_checkmark"
- android:tint="@color/blue_main" />
+ android:src="@drawable/ic_stopwatch" />
+ android:src="@drawable/ic_smartphone" />
+ android:src="@drawable/ic_location_pin" />
+ android:src="@drawable/ic_stopwatch" />
Date: Tue, 25 May 2021 20:03:08 +0200
Subject: [PATCH 102/207] Implemented Modal transitions and screens
---
.../java/ch/admin/bag/dp3t/MainActivity.java | 2 +-
.../checkin/checkinflow/CheckInFragment.kt | 24 ++++++++++++-------
.../checkinflow/QrCodeScannerFragment.java | 2 +-
.../generateqrcode/EventsOverviewFragment.kt | 6 ++---
.../checkin/generateqrcode/QrCodeFragment.kt | 19 +++++++++------
.../bag/dp3t/extensions/FragmentExtensions.kt | 17 ++++++++++---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 5 ++--
app/src/main/res/anim/modal_pop_enter.xml | 8 +++----
app/src/main/res/anim/modal_pop_exit.xml | 1 +
app/src/main/res/anim/modal_slide_enter.xml | 2 ++
app/src/main/res/anim/modal_slide_exit.xml | 14 +++--------
app/src/main/res/layout/fragment_check_in.xml | 23 ++++++++++++++++++
app/src/main/res/layout/fragment_qr_code.xml | 24 +++++++++++++++----
13 files changed, 101 insertions(+), 46 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
index 3f88dafea..1752ce63b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.java
@@ -307,7 +307,7 @@ private void showCheckOutFragment() {
private void showCheckInScreen() {
getSupportFragmentManager().beginTransaction()
- .replace(R.id.main_fragment_container, CheckInFragment.newInstance())
+ .replace(R.id.main_fragment_container, CheckInFragment.newInstance(false))
.addToBackStack(CheckInFragment.class.getCanonicalName())
.commit();
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
index 7b918d99d..2d8017db6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
@@ -7,13 +7,15 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
+import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.activityViewModels
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.generateqrcode.QrCodeFragment
+import ch.admin.bag.dp3t.checkin.generateqrcode.EventsOverviewFragment
import ch.admin.bag.dp3t.checkin.models.ReminderOption
import ch.admin.bag.dp3t.checkin.utils.*
import ch.admin.bag.dp3t.databinding.FragmentCheckInBinding
@@ -24,15 +26,18 @@ import ch.admin.bag.dp3t.extensions.getSubtitle
import com.google.android.material.button.MaterialButton
import org.crowdnotifier.android.sdk.model.VenueInfo
+private const val ARG_IS_SELF_CHECKIN = "ARG_IS_SELF_CHECKIN"
+
class CheckInFragment : Fragment() {
companion object {
val TAG = CheckInFragment::class.java.canonicalName
@JvmStatic
- fun newInstance(): CheckInFragment {
- return CheckInFragment()
+ fun newInstance(isSelfCheckin: Boolean = false) = CheckInFragment().apply {
+ arguments = bundleOf(ARG_IS_SELF_CHECKIN to isSelfCheckin)
}
+
}
private val viewModel: CrowdNotifierViewModel by activityViewModels()
@@ -85,6 +90,11 @@ class CheckInFragment : Fragment() {
}
toolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
+ cancelButton.setOnClickListener {
+ requireActivity().supportFragmentManager.popBackStack(EventsOverviewFragment::class.java.canonicalName, 0)
+ }
+ selfCheckinToolbar.isVisible = requireArguments().getBoolean(ARG_IS_SELF_CHECKIN)
+ toolbar.isVisible = !requireArguments().getBoolean(ARG_IS_SELF_CHECKIN)
}.root
}
@@ -101,12 +111,10 @@ class CheckInFragment : Fragment() {
}
private fun popBackToHomeFragment() {
- val fm = requireActivity().supportFragmentManager
- val backStackEntry = if (fm.backStackEntryCount >= 2) fm.getBackStackEntryAt(fm.backStackEntryCount - 2) else null
- if (backStackEntry?.name == QrCodeFragment::class.java.canonicalName) {
- fm.popBackStack(CheckinOverviewFragment::class.java.canonicalName, 0)
+ if (requireArguments().getBoolean(ARG_IS_SELF_CHECKIN)) {
+ requireActivity().supportFragmentManager.popBackStack(CheckinOverviewFragment::class.java.canonicalName, 0)
} else {
- fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+ requireActivity().supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
index 5d3b95240..48c7b10f6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -260,7 +260,7 @@ private void setIndicatorColor(View indicator, @ColorRes int color) {
private void showCheckInFragment() {
requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
- .replace(R.id.main_fragment_container, CheckInFragment.newInstance())
+ .replace(R.id.main_fragment_container, CheckInFragment.newInstance(false))
.addToBackStack(CheckInFragment.class.getCanonicalName())
.commitAllowingStateLoss();
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
index a51ff45ed..8487526e6 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
@@ -20,17 +20,17 @@ class EventsOverviewFragment : Fragment() {
private val qrCodeViewModel: QRCodeViewModel by activityViewModels()
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FragmentEventsOverviewBinding.inflate(layoutInflater).apply {
eventsToolbar.setNavigationOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
val adapter = QrCodeAdapter(object : OnClickListener {
override fun generateQrCode() {
- showFragment(GenerateQrCodeFragment.newInstance())
+ showFragment(GenerateQrCodeFragment.newInstance(), modalAnimation = true)
}
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
- showFragment(QrCodeFragment.newInstance(qrCodeItem))
+ showFragment(QrCodeFragment.newInstance(qrCodeItem), modalAnimation = true)
}
override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index 21b0cbee1..f76f7d62d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -25,7 +25,6 @@ import ch.admin.bag.dp3t.extensions.toQrCodePayload
import ch.admin.bag.dp3t.extensions.toVenueInfo
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
import ch.admin.bag.dp3t.extensions.combineWith
-import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import com.google.protobuf.ByteString
import org.crowdnotifier.android.sdk.model.VenueInfo
@@ -48,7 +47,7 @@ class QrCodeFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
- toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
+ cancelButton.setOnClickListener { parentFragmentManager.popBackStack() }
val venueInfo = QRCodePayload.parseFrom(arguments?.get(KEY_VENUE_INFO) as ByteString).toVenueInfo()
titleTextview.text = venueInfo.title
subtitleTextview.setText(venueInfo.getSubtitle())
@@ -79,14 +78,20 @@ class QrCodeFragment : Fragment() {
}.observe(viewLifecycleOwner) { showCheckinButton ->
checkinButton.isVisible = showCheckinButton
}
- checkinButton.setOnClickListener {
- crowdNotifierViewModel.checkInState =
- CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), 0)
- showFragment(CheckInFragment.newInstance())
- }
+ checkinButton.setOnClickListener { showCheckInFragment(venueInfo) }
}.root
}
+ private fun showCheckInFragment(venueInfo: VenueInfo) {
+ crowdNotifierViewModel.checkInState =
+ CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), 0)
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit)
+ .replace(R.id.main_fragment_container, CheckInFragment.newInstance(isSelfCheckin = true))
+ .addToBackStack(CheckInFragment::class.java.canonicalName)
+ .commit()
+ }
+
private fun printPdf(file: File) {
context?.let {
val manager = it.getSystemService(Context.PRINT_SERVICE) as PrintManager
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
index 64218cc0e..b0aacf6e0 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
@@ -7,11 +7,22 @@ import ch.admin.bag.dp3t.R
fun Fragment.showFragment(
fragment: Fragment,
@IdRes container: Int = R.id.main_fragment_container,
- addToBackStack: Boolean = true
+ addToBackStack: Boolean = true,
+ modalAnimation: Boolean = false
) {
requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ .apply {
+ if (modalAnimation) {
+ this.setCustomAnimations(
+ R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit
+ )
+ } else {
+ this.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
+ }
+ if (addToBackStack) {
+ this.addToBackStack(fragment::class.java.canonicalName)
+ }
+ }
.replace(container, fragment)
- .apply { if (addToBackStack) this.addToBackStack(fragment::class.java.canonicalName) }
.commit()
}
\ No newline at end of file
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 f539c0417..901fbda56 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
@@ -259,9 +259,10 @@ private void showReportsFragment() {
int tracingReports = tracingViewModel.getAppStatusLiveData().getValue().getExposureDays().size();
boolean isReportedPositive = tracingViewModel.getTracingStatusInterface().isReportedAsInfected();
if (((checkinReports > 0 && tracingReports > 0) || checkinReports > 1) && !isReportedPositive) {
- FragmentExtensionsKt.showFragment(this, ReportsOverviewFragment.newInstance(), R.id.main_fragment_container, true);
+ FragmentExtensionsKt
+ .showFragment(this, ReportsOverviewFragment.newInstance(), R.id.main_fragment_container, true, false);
} else {
- FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), R.id.main_fragment_container, true);
+ FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), R.id.main_fragment_container, true, false);
}
}
diff --git a/app/src/main/res/anim/modal_pop_enter.xml b/app/src/main/res/anim/modal_pop_enter.xml
index e6d7ff503..3fcf950f1 100644
--- a/app/src/main/res/anim/modal_pop_enter.xml
+++ b/app/src/main/res/anim/modal_pop_enter.xml
@@ -1,11 +1,9 @@
-
+ android:fromAlpha="0"
+ android:toAlpha="1" />
\ No newline at end of file
diff --git a/app/src/main/res/anim/modal_pop_exit.xml b/app/src/main/res/anim/modal_pop_exit.xml
index 53a435703..a9dd3d1be 100644
--- a/app/src/main/res/anim/modal_pop_exit.xml
+++ b/app/src/main/res/anim/modal_pop_exit.xml
@@ -5,6 +5,7 @@
android:duration="200"
android:fillAfter="true"
android:fromYDelta="0%p"
+ android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="100%p" />
\ No newline at end of file
diff --git a/app/src/main/res/anim/modal_slide_enter.xml b/app/src/main/res/anim/modal_slide_enter.xml
index 900ed44de..57b4fa014 100644
--- a/app/src/main/res/anim/modal_slide_enter.xml
+++ b/app/src/main/res/anim/modal_slide_enter.xml
@@ -3,6 +3,8 @@
+
diff --git a/app/src/main/res/anim/modal_slide_exit.xml b/app/src/main/res/anim/modal_slide_exit.xml
index e97f753a3..1a9bf5c7e 100644
--- a/app/src/main/res/anim/modal_slide_exit.xml
+++ b/app/src/main/res/anim/modal_slide_exit.xml
@@ -1,17 +1,9 @@
-
-
+ android:fromAlpha="1"
+ android:toAlpha="0" />
diff --git a/app/src/main/res/layout/fragment_check_in.xml b/app/src/main/res/layout/fragment_check_in.xml
index fc9da5ef6..dd324fc5f 100644
--- a/app/src/main/res/layout/fragment_check_in.xml
+++ b/app/src/main/res/layout/fragment_check_in.xml
@@ -8,6 +8,29 @@
android:background="@color/white"
android:orientation="vertical">
+
+
+
+
+
+
-
+ android:background="@color/white"
+ android:elevation="@dimen/card_elevation">
+
+
+
+
+
Date: Tue, 25 May 2021 22:18:29 +0200
Subject: [PATCH 103/207] Use TestInformationUrl from Config Response
---
.../admin/bag/dp3t/networking/ConfigWorker.kt | 2 ++
.../models/ConfigResponseModel.java | 5 +++++
.../admin/bag/dp3t/reports/ReportsFragment.kt | 5 +----
.../admin/bag/dp3t/storage/SecureStorage.java | 19 +++++++++++++++++++
4 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt b/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
index 8ffc7bd2f..6e11b2025 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/ConfigWorker.kt
@@ -79,6 +79,8 @@ class ConfigWorker(context: Context, workerParams: WorkerParameters) : Coroutine
secureStorage.hasInfobox = false
}
+ secureStorage.setTestInformationUrls(config.testInformationUrls)
+
secureStorage.testLocations = config.testLocations
secureStorage.interopCountries = config.interOpsCountries
diff --git a/app/src/main/java/ch/admin/bag/dp3t/networking/models/ConfigResponseModel.java b/app/src/main/java/ch/admin/bag/dp3t/networking/models/ConfigResponseModel.java
index 9a2938ca1..07eaf0c80 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/networking/models/ConfigResponseModel.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/networking/models/ConfigResponseModel.java
@@ -20,6 +20,7 @@ public class ConfigResponseModel {
private SdkConfigModel androidGaenSdkConfig;
private Map> testLocations;
private List interOpsCountries;
+ private Map testInformationUrls;
public boolean getDoForceUpdate() {
return forceUpdate;
@@ -50,4 +51,8 @@ public List getInterOpsCountries() {
return interOpsCountries;
}
+ public Map getTestInformationUrls() {
+ return testInformationUrls;
+ }
+
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
index 1b5fbdffa..041a435f2 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
@@ -50,9 +50,6 @@ import kotlin.math.min
private const val ARG_CHECK_IN_ID = "ARG_CHECK_IN_ID"
private const val ARG_SHOW_TRACING_REPORT_DETAILS = "ARG_SHOW_TRACING_REPORT_DETAILS"
-private const val TESTING_EXTERNAL_LINK =
- "https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
class ReportsFragment : Fragment() {
@@ -320,7 +317,7 @@ class ReportsFragment : Fragment() {
}
private fun showTestInformation() {
- UrlUtil.openUrl(context, TESTING_EXTERNAL_LINK)
+ UrlUtil.openUrl(context, secureStorage.getTestInformationUrl(getString(R.string.language_key)))
}
private fun callHotline() {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
index 090ab0fb0..31a8200d5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
@@ -76,6 +76,7 @@ public class SecureStorage {
private static final String KEY_CURRENT_CHECK_IN = "KEY_CURRENT_CHECK_IN";
private static final String KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG = "KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG";
private static final String KEY_LAST_SUCCESSFUL_CHECKIN_DOWNLOAD = "KEY_LAST_SUCCESSFUL_CHECKIN_DOWNLOAD";
+ private static final String KEY_CONFIG_TEST_INFORMATION_URLS = "testinformation_urls";
private static SecureStorage instance;
@@ -228,6 +229,24 @@ public InfoBoxModelCollection getInfoBoxCollection() {
return gson.fromJson(prefs.getString(KEY_CONFIG_INFOBOX_COLLECTION, "null"), InfoBoxModelCollection.class);
}
+ public void setTestInformationUrls(Map testInformationUrls) {
+ prefs.edit().putString(KEY_CONFIG_TEST_INFORMATION_URLS, gson.toJson(testInformationUrls)).apply();
+ }
+
+ public String getTestInformationUrl(String languageKey) {
+ String defaultUrl = "https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/" +
+ "aktuelle-ausbrueche-epidemien/novel-cov/testen.html";
+
+ Type testInformationsType = new TypeToken"Alle auswählen""Nicht senden"
- "Erneut versuchen""Nicht teilen""Wirklich nicht teilen?""Das Teilen Ihrer privaten Schlüssel hilft «SwissCovid», andere potenziell infizierte Personen zu benachrichtigen und die weitere Ausbreitung des Virus zu verhindern. Wenn Sie Ihre privaten Schlüssel nicht teilen, werden keine potenziell infizierten Personen benachrichtigt."
@@ -1702,18 +1701,6 @@ Dann rufen Sie hier an:""Ihre ausgewählten Check-In-Einträge wurden gesendet. Alle Personen, die zum gleichen Zeitpunkt am gleichen Ort eingecheckt haben, werden benachrichtigt."
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
-
"SwissCovid-Check-in"
@@ -1853,4 +1840,9 @@ Dieser Titel wird beim Check-in angezeigt.""Konfiguration abgeschlossen""Vielen Dank, dass Sie mithelfen, die Verbreitung des Coronavirus einzudämmen.""Bei einem Ansteckungsrisiko schnell informiert."
+
+
+
+ "https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+ "Für diesen Check-In gilt eine maximale Check-in Dauer von {DURATION}. Bitte überprüfen Sie nochmals Ihre Angabe."
\ 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 3bddafd99..66d28d707 100644
--- a/common/src/main/res/values-es/strings.xml
+++ b/common/src/main/res/values-es/strings.xml
@@ -104,7 +104,7 @@ De esta forma, está ayudando a parar las cadenas de contagio.""Posible infección"
- "Existe la posibilidad de un contagio."
+ "Es posible que se haya contagiado""Aplicación activa"
@@ -194,7 +194,7 @@ De esta forma, está ayudando a parar las cadenas de contagio.""Con el código Covid…"
- "Parar las cadenas de contagio"
+ "Advertir de posibles contagios""Introducir el código Covid"
@@ -228,7 +228,7 @@ De esta forma, está ayudando a parar las cadenas de contagio.""Rastreo desactivado""Active el rastreo para que la aplicación funcione.""Bluetooth desactivado"
- "Para que el rastreo funcione, active Bluetooth en su dispositivo."
+ "Para que el rastreo anónimo funcione, active Bluetooth en su dispositivo.""Activar Bluetooth""Rechazada la utilización de Bluetooth""Autorice la utilización de Bluetooth para permitir el funcionamiento del rastreo"
@@ -260,11 +260,11 @@ De esta forma, está ayudando a parar las cadenas de contagio."
"Siga las instrucciones de aislamiento recibidas."
- "Rastreo terminado"
- "No se guardan más contactos."
+ "Contactos y Check-in desactivados"
+ "No se guardan contactos""Contacte""Siga las "
- "instrucciones para el autoaislamiento"
+ "Instrucciones para el aislamiento""Nueva notificación"
@@ -274,7 +274,7 @@ De esta forma, está ayudando a parar las cadenas de contagio."
"Test positivo"
- "Existe la posibilidad de un contagio."
+ "Es posible que se haya contagiado""A dado usted positivo en el test del coronavirus"
@@ -345,7 +345,7 @@ Infoline SwissCovid
"Los ID aleatorios guardados permanecen en su teléfono móvil y son eliminados transcurridos 14 días."
- "¿Cómo funciona la aplicación?"
+ "Contactos""Detección de contactos a través de Bluetooth"
@@ -396,7 +396,7 @@ Infoline SwissCovid
"Permitir mensajes"
- "Para que la aplicación pueda informarle, debe activar los mensajes"
+ "Para que la aplicación pueda avisarle, debe activar los mensajes""Activar mensajes"
@@ -419,7 +419,7 @@ Siga las instrucciones de aislamiento recibidas."
"La aplicación está instalada"
- "Gracias por ayudar a romper las cadenas de contagio."
+ "Gracias por contribuir a contener la propagación del coronavirus.""Comenzar"
@@ -446,7 +446,7 @@ Siga las instrucciones de aislamiento recibidas."
"Posible infección"
- "Puede haber ocurrido una infección"
+ "Es posible que se haya contagiado""Error en la hora"
@@ -578,9 +578,9 @@ Siga las instrucciones de aislamiento recibidas."
"¿Qué son las notificaciones?"
- "La aplicación controla regularmente si alguno de los usuarios con los que se ha encontrado ha notificado un test positivo.
+ "La aplicación controla regularmente si alguno de los usuarios con los que se ha encontrado o que se han registrado en el mismo sitio que usted, ha notificado un test positivo.
-En caso de un posible contacto, será informado con un mensaje en la pantalla de bloqueo."
+En caso de un posible contagio, será informado con un mensaje en la pantalla de bloqueo.""La Infoline SwissCovid le asesora sobre la posibilidad de realizar un test gratuito."
@@ -619,7 +619,7 @@ Así queda garantizado que solo se notifiquen casos confirmados a través de la
"¿Qué es lo que se envía?"
- "Solamente se envían las claves privadas de la aplicación y no los datos personales.
+ "Solamente se envían las claves privadas de la aplicación, así como los identificadores codificados de los Check-in, no los datos personales.
De esta forma, se informa a otros usuarios de la aplicación SwissCovid de que existe la posibilidad de contagio."
@@ -630,7 +630,7 @@ De esta forma, se informa a otros usuarios de la aplicación SwissCovid de que e
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Aviso importante"
+ "Información sobre el registro""A pesar de que no se envían datos sobre su persona, es posible que alguien recuerde por la fecha haber tenido contacto con usted."
@@ -663,7 +663,7 @@ Pueden aparecer también los síntomas siguientes:
"¿Por qué está desactivado el rastreo?"
- "El rastreo en la aplicación se desactiva automáticamente tras un resultado positivo. El rastreo puede volver a activarse, tan pronto como haya acabado el aislamiento."
+ "El rastreo en la aplicación se desactiva automáticamente tras un resultado positivo. El rastreo puede volver a activarse tan pronto como haya acabado el aislamiento.""es"
@@ -703,8 +703,7 @@ Pueden aparecer también los síntomas siguientes:
"Activar el rastreo"
-
- "Para poder utilizar la aplicación debe activarse Notificaciones de exposición a la COVID-19"
+ "La función de rastreo debe estar activada a fin de detectar los contactos.""Activar"
@@ -736,7 +735,7 @@ Pueden aparecer también los síntomas siguientes:
"¿De verdad quiere eliminar esta notificación?""Ignorar optimización de la batería"
- "Para poder detectar los contactos en todo momento, la optimización de la batería debe estar desconectada para esta aplicación."
+ "La aplicación solo funciona si la optimización de la batería está desconectada.""Autorizar""OK""Solo para esta aplicación"
@@ -759,8 +758,7 @@ Pueden aparecer también los síntomas siguientes:
"Notificaciones de exposición a la COVID-19 desactivado desactivado"
-
- "Active la función Notificaciones de exposición a la COVID-19, para que el rastreo pueda funcionar."
+ "Active {TRACING_SETTING_TEXT} para que el rastreo anónimo pueda funcionar.""No se ha podido enviar el mensaje. Autorice el intercambio de su ID aleatoria con la app SwissCovid."
@@ -802,10 +800,10 @@ Si la actualización no estuviera disponible en su dispositivo actualmente, int
"Swisscovid permanece activa aún cuando se cierra la aplicación."
- "SwissCovid
-para las
-cadenas de contagio."
- "A pesar de que no se envían datos sobre su persona, es posible que alguien recuerde por la fecha haber tenido contacto con usted."
+ "SwissCovid -
+información rápida
+ante riesgo de contagio."
+ "Sí, pero aunque no se envían datos sobre su persona, es posible que alguien recuerde por la fecha haber tenido contacto con usted.""¿Permanezco en el anonimato?"
@@ -815,7 +813,7 @@ cadenas de contagio.""Notificaciones de exposición a la COVID-19"
- "Para poder utilizar la aplicación debe activarse Notificaciones de exposición a la COVID-19"
+ "La función de rastreo debe estar activada a fin de detectar los contactos.""SwissCovid"
@@ -908,7 +906,7 @@ Schwarzenburgstrasse 157
"Viajar"
- "SwissCovid funciona en los países siguientes:"
+ "La función SwissCovid-Contactos funciona en los países siguientes:""¿Y esto qué significa?"
@@ -1225,8 +1223,8 @@ Al fin de poder alertar también a los viajeros del extranjero, esto ocurre inde
"Finalizar"
- "No se registran más contactos.
-El rastreo puede volver a activarse una vez finalizado en aislamiento."
+ "No se registran más contactos. El Check-in tampoco está disponible.
+Ambas funciones pueden volver a activarse una vez finalizado en aislamiento.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1258,7 +1256,7 @@ El rastreo puede volver a activarse una vez finalizado en aislamiento."
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "¿Es usted sordo o tiene una discapacidad auditiva y no puede llamar a la Infoline del coronavirus?
+ "¿Es usted sordo o tiene una discapacidad auditiva y no puede llamar por teléfono?
Envíe un correo electrónico a covid-support@medgate.ch"
@@ -1314,7 +1312,7 @@ El tiempo es un factor determinante a la hora de luchar contra el coronavirus: c
"¿Ha dado usted positivo (test PCR o test rápido de antígenos) y no ha recibido el código Covid después de 4 horas?
-Contacte con la Infoline del coronavirus:"
+Llame a este número:""¿Aún no ha recibido el código Covid?"
@@ -1338,7 +1336,7 @@ Contacte con la Infoline del coronavirus:"
"Países compatibles:"
- "A fin de alertar también a viajeros del extranjero, las claves privadas se comparten con las aplicaciones contra el coronavirus de países compatibles."
+ "A fin de alertar también a viajeros del extranjero tras un contacto, las claves privadas se comparten con las aplicaciones contra el coronavirus de países compatibles.""Total"
@@ -1353,13 +1351,13 @@ Contacte con la Infoline del coronavirus:"
"¿Quién será notificado?"
- "Se notificará a todos los contactos establecidos durante la fase de contagio desde el {ONSET_DATE} hasta la introducción del código Covid si un contagio es probable a raíz de la distancia y la duración."
+ "Se notificará a todos los contactos establecidos durante la fase de contagio desde el {ONSET_DATE} hasta la introducción del código Covid si existe un riesgo de contagio - a raíz de la distancia y la duración del contacto, así como en el caso de haberse registrado en el mismo sitio.""{ONSET_DATE} – hoy"
- "Así está contribuyendo a interrumpir las cadenas de contagio."
+ "Así está contribuyendo a avisar a otras personas de un posible contagio.""Las claves privadas de su aplicación se han enviado para el periodo siguiente:"
@@ -1398,365 +1396,362 @@ Contacte con la Infoline del coronavirus:"
"En 1 día"
-
+ "Recordatorio Check-out"
-
+ "¿Quiere hacer el Check-out?"
-
+ "Check-out automático"
-
+ "Su salida ha sido registrada automáticamente."
-
+ "Escanear el código QR y registrarse"
-
+ "¡Código QR no válido!"
-
+ "De"
-
+ "hasta"
-
+ "Añadir nota"
-
+ "por ej. ¿Quién le acompañaba?"
-
+ "Listo"
-
+ "Recordatorio Check-out"
-
+ "Off"
-
+ "Autentifíquese para acceder al diario"
-
+ "{HOURS} h"
-
+ "{HOURS} h"
-
+ "La app debe acceder a la cámara del dispositivo para poder escanear el código QR."
-
+ "Mis Check-in"
-
+ "Check-in"
-
+ "Se necesita la cámara para escanear el código QR."
-
+ "Habilite la función Face-ID para acceder a su diario con total seguridad."
-
+ "Autentifíquese para acceder al diario"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generar código QR"
-
+ "¿Quiere ofrecer Check-in a otros?"
-
+ "Check-in ahora"
-
+ "No se podido fijar el recordatorio"
-
+ "Para poder recibir los recordatorios para el registro de salida, debe habilitar \\"Notificaciones\\" en los ajustes de la aplicación. "
-
+ "Ajustar"
-
+ "Editar"
-
+ "Aún no hay entradas"
-
+ "Aquí se genera de modo automático un registro al escanear un código QR SwissCovid cuando hace un Check-in in situ."
-
+ "SwissCovid debe acceder a la cámara de su dispositivo. La cámara se utiliza solo para escanear el código QR."
-
+ "Permitir el acceso a la cámara"
-
+ "por ej. lugar o nombre del evento"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Imprimir PDF"
+ "Información"
+ "Error de red"
+ "Compruebe su conexión a internet"
+ "Volver a intentar"
+ "Notificaciones desactivadas"
+ "Modifique los ajustes para recibir los avisos directamente."
+ "Modificar ajustes"
+ "No es posible acceder a la cámara"
+ "La app debe acceder a la cámara para escanear el código QR."
+ "Error"
+ "Código QR aún no válido"
+ "Código QR ya no es válido"
+ "Ya ha registrado su entrada."
+ "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"
+ "Check out"
+ "Snooze"
+ "Notificación de Check-in permanente"
+ "Está usted registrado desde las {TIME} horas."
-
+ "Guardar"
-
+ "Escanear código QR"
-
+ "Check-in desactivado"
-
+ "El Check-in no es posible actualmente"
-
+ "Escanee un código QR SwissCovid para hacer el Check-in in situ."
-
+ "Aquí se genera de modo automático un registro si escanea usted un código QR SwissCovid al hacer un Check-in in situ."
-
-
-
+ "Esta usted registrado"
+ "{DATE1} hasta {DATE2}"
+ "Más información sobre el código Covid"
-
+ "Eliminar registro"
-
+ "Eliminar"
-
+ "Compartir"
-
+ "Imprimir"
-
+ "Escanee un código QR SwissCovid para hacer el registro de entrada in situ."
-
+ "Compartir registros de Check-in"
-
+ "Al compartir los registros de Check-in, la aplicación puede informar a personas que se registraron al mismo tiempo en el mismo sitio."
-
-
-
-
-
-
+ "Seleccionar todos"
+ "No enviar"
+ "No compartir"
+ "¿Seguro que no quiere compartir?"
+ "Al compartir sus claves privadas, «SwissCovid» puede alertar a otras personas potencialmente contagiadas y evitar así la propagación del virus. Si no comparte sus claves privadas no es posible alertar a otras personas que pueden haberse contagiado."
-
+ "Eliminar registro"
-
+ "El registro será eliminado irreversiblemente de la lista."
-
+ "Eliminar por completo"
-
+ "¿Solo ocultar y seguir recibiendo avisos?"
-
+ "¿Ha dado positivo?"
-
+ "Volver a activar contactos y Check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "No puede usted hacer un registro de entrada si ha dado positivo en el test."
-
+ "Genere un código QR para permitir que las personas puedan registrase in situ."
-
+ "¿Seguro que quiere eliminar el código QR?"
-
+ "Auto Check-in"
-
+ "Escanee un código QR SwissCovid para registrarse in situ."
-
+ "No es posible guardar"
-
+ "Las horas seleccionadas coinciden con otros Check-in en su diario. Por favor, compruebe la entrada."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "Se han enviado los registros de Check-in seleccionados. Todas las personas que se registraron al mismo tiempo en el mismo sitio recibirán un aviso. "
-
+ "SwissCovid Check-in"
-
+ "Check-in sin dejar datos"
-
+ "Con la app SwissCovid puede usted registrarse fácilmente sin necesidad de dejar sus datos. Ante una situación de riesgo, será usted informado rápidamente a través de la aplicación."
-
+ "App SwissCovid"
-
+ "Descargar ahora"
-
+ "Registro de entrada sin dejar datos"
-
+ "Introduzca aquí el código Covid para alertar a otras personas."
-
+ "¿Finalizado el aislamiento?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "¿Quiere usted ofrecer Check-in a otros?"
+ "¿Por qué una función Check-in?"
+ "La función Check-in sin registro de datos de contacto es una ampliación de la app SwissCovid. En caso de dar positivo en un test, esta función le permite alertar no solo a los contactos estrechos, sino también a personas que se registraron al mismo tiempo en el mismo lugar que usted."
+ "Sin almacenamiento centralizado de datos"
+ "Para los Check-in de la app SwissCovid no es necesario indicar datos personales como el nombre, la dirección o el número de teléfono. Tampoco se registran datos GPS de geolocalización. La lista con los Check-in se guarda en su dispositivo. Estos datos locales se eliminan transcurridos 14 días."
+ "Genere un código QR"
+ "Con un código QR SwissCovid es posible registrarse in situ sin necesidad de dejar datos personales."
+ "No sustituye el registro obligatorio de los datos de contacto"
+ "Los Check-in SwissCovid nos sustituyen el registro obligatorio de los datos de contacto estipulado en la «Ordenanza Covid-19, situación particular». "
+ "¿Dónde utilizar los Check-in?"
+ "¿Por qué una función Check-in?"
+ "Los Check-in SwissCovid puede utilizarse allí donde las personas se reúnen pero el registro de los datos de contacto no es obligatorio.
+
+Posibles utilizaciones:
+– eventos privados
+– eventos de asociaciones
+– reuniones en el entorno laboral, por ej. salas de reuniones, oficinas, cantinas, etc."
+ "La función Check-in sin registro de datos de contacto es una ampliación de la app SwissCovid. En caso de dar positivo en un test, esta función le permite alertar no solo a los contactos estrechos, sino también a personas que se registraron al mismo tiempo"
-
+ "Situaciones de riesgo"
-
+ "Contactos"
-
+ "Check-in"
-
+ "¿Qué debo hacer?"
-
+ "Check-in positivo"
-
+ "Check-in positivos / contactos"
-
+ "Código QR usuario"
-
+ "Código QR rastreo de contacto"
-
+ "La función Check-in se desactiva automáticamente en caso de dar positivo en un test. La función puede volver a activarse una vez finalizado el aislamiento."
-
-
-
-
-
-
-
-
+ "Elija un título que describa bien el lugar o el evento. Este título aparecerá en el Check-in. "
+ "¿Qué puede usted hacer?"
+ "Proteger a sus contactos"
+ "Estar atento a los síntomas"
+ "Hacer el test"
+ "Es posible que sea usted contagioso sin darse cuenta. Observe las reglas de higiene y comportamiento y proteja a su familia, sus amigos y su entorno."
+ "Vigile su estado de salud."
+ "Ante la aparición de síntomas haga el test inmediatamente. Incluso si no tiene síntomas, es oportuno y recomendable hacer el test de Covid-19."
-
+ "Check-in"
-
-
-
-
-
+ "No se han enviado datos"
+ "No se han enviado claves privadas."
+ "Volver"
+ "No enviar datos"
+ "No se han enviado registros de Check-in."
-
-
-
-
-
-
-
-
+ "Compartir la selección"
+ "No compartir nada"
+ "Check-in sin dejar datos"
+ "Registros de Check-in"
+ "La función Check-in SwissCovid le permite registrarse con la aplicación en un lugar o en un evento."
+ "Su presencia solo queda registra en la aplicación. No es necesario indicar datos personales en la app."
+ "La función Check-in SwissCovid le permite registrarse con la aplicación en un lugar o en un evento. Su presencia solo queda registrada en la aplicación. No es necesario indicar datos personales en la app. A estos efectos se han actualizado las condiciones de uso y la política de privacidad."
+ "Omitir"
-
+ "El registro permanece codificado en la aplicación en segundo plano durante 14 días. En caso de un posible contagio puede usted recibir un aviso."
-
-
+ "Solo ocultar"
+ "La app puede recordarle que registre la salida en el momento adecuado."
-
+ "¿O eliminar por completo?"
-
-
+ "El registro será eliminado por completo. A partir de ahora no recibirá ningún aviso."
+ "Nadie será avisado."
-
-
-
-
-
-
-
+ "¿Qué debo saber sobre el test?"
+ "Instalar la app y Check-in "
+ "Cierre la configuración de la aplicación para poder utilizar la función de rastreo."
+ "Cerrar ahora"
+ "Configuración finalizada"
+ "Gracias por contribuir a contener la propagación del coronavirus."
+ "Información rápida ante un riesgo de contagio."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 0f5c72a0b..c3f7a79ef 100644
--- a/common/src/main/res/values-fr/strings.xml
+++ b/common/src/main/res/values-fr/strings.xml
@@ -104,7 +104,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Infection possible"
- "Il y a un risque d'infection."
+ "Vous pourriez avoir contracté le virus.""Application active"
@@ -194,7 +194,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Avec le code COVID…"
- "Interrompre les chaînes d'infection"
+ "Avertir d’une infection possible""Entrer le code COVID"
@@ -228,7 +228,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Traçage de proximité désactivé""Pour que l'application fonctionne, activez le traçage de proximité.""Bluetooth désactivé"
- "Pour que le traçage de proximité fonctionne, activez Bluetooth sur votre appareil."
+ "Pour que le traçage anonyme fonctionne, le Bluetooth doit être activé sur votre smartphone.""Bluetooth activé.""Bluetooth : utilisation refusée""Pour que le traçage de proximité fonctionne, autorisez l'utilisation de Bluetooth."
@@ -260,11 +260,11 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Suivez les consignes reçues sur l'isolement."
- "Traçage de proximité terminé"
- "Plus aucun contact ne sera sauvegardé."
+ "Contacts et connexion désactivés"
+ "Aucune contact n’est sauvegardé.""Contactez""Suivez"
- "Consignes sur l'auto-isolement"
+ "Consignes sur l’isolement""Nouvelle alerte"
@@ -274,7 +274,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Test positif"
- "Il y a un risque d'infection."
+ "Vous pourriez avoir contracté le virus.""Vous avez été testé positif au coronavirus."
@@ -328,7 +328,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Grâce à l'application SwissCovid, tout le monde peut contribuer à endiguer la propagation du virus."
- "L'application vous informe si vous avez été potentiellement exposé au coronavirus."
+ "L’application vous avertit en cas d’exposition possible au coronavirus.""Privacy by Design"
@@ -343,7 +343,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Les ID aléatoires enregistrés restent sur votre appareil et sont supprimés après 14 jours."
- "Comment fonctionne l'application ?"
+ "Contacts""Détection des contacts via Bluetooth"
@@ -361,7 +361,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Notification en cas de possible infection"
- "L'application vous informe si vous avez été potentiellement exposé au coronavirus."
+ "L’application vous avertit en cas d’exposition possible au coronavirus.""En adoptant une bonne conduite, vous pouvez interrompre les chaînes de transmission et protéger les autres."
@@ -394,7 +394,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Autoriser les notifications"
- "Vous devez autorisez les notifications pour recevoir des informations de la part de l'application."
+ "Pour que l’application puisse vous avertir, vous devez autoriser les notifications.""Autoriser les notifications"
@@ -417,7 +417,7 @@ Respectez les consignes reçues sur l'isolement."
"L'application est prête."
- "Merci d'aider à interrompre les chaînes de transmission."
+ "Merci d’aider à endiguer la propagation du coronavirus.""Commencer"
@@ -444,7 +444,7 @@ Respectez les consignes reçues sur l'isolement."
"Infection possible"
- "Il y a un risque d'infection."
+ "Vous pourriez avoir contracté le virus.""Erreur dans l'heure"
@@ -583,9 +583,9 @@ Ces identifiants sont enregistrés localement dans les appareils et effacés apr
"Que sont les notifications ?"
- "L'application contrôle régulièrement si d'autres utilisateurs que vous avez rencontrés ont déclaré un test positif.
+ "L’application contrôle régulièrement si d’autres utilisateurs que vous avez rencontrés ou qui se sont connectés au même endroit que vous ont signalé un test positif.
-En cas de possible infection, vous serez informé par message sur l'écran de verrouillage."
+Un message s’affichera sur l’écran de verrouillage pour vous informer d’une possible infection.""L'infoline SwissCovid vous conseille sur la possibilité d'effectuer un test gratuit."
@@ -624,9 +624,10 @@ Ceci permet de garantir que seuls les cas confirmés sont déclarés dans l'appl
"Qu'est-ce qui est envoyé ?"
- "Seules les clés privées de votre application sont envoyées, aucune donnée personnelle.
+ "Seules les clés privées de votre application et les identifiants codés de vos connexions sont transmises, aucune donnée personnelle.
-Ainsi, d'autres utilisateurs de l'application SwissCovid apprennent qu'ils ont peut-être été infectés."
+Ainsi, d’autres utilisateurs de l’application SwissCovid apprennent qu’il y a un risque d’infection.
+""+41 58 387 77 78"
@@ -635,10 +636,10 @@ Ainsi, d'autres utilisateurs de l'application SwissCovid apprennent qu'ils ont p
"https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/fr/categories/application-swisscovid"
- "Avis important"
+ "Information concernant la saisie"
- "Même si aucune donnée personnelle n'est envoyée sur vous, il est possible que quelqu'un se souvienne du contact sur la base de la date."
+ "Même si aucune donnée personnelle n’est transmise à votre sujet, il se peut que, grâce à la date, un utilisateur se souvienne de la rencontre.""D'accord"
@@ -669,7 +670,7 @@ Les symptômes suivants peuvent aussi apparaître :
"Pourquoi le traçage est-il désactivé ?"
- "Le traçage est automatiquement désactivé après un test positif. Vous pourrez le réactiver à la fin de votre isolement."
+ "La fonction de traçage de l’application est automatiquement désactivée après un test positif. Vous pouvez la réactiver dès la fin de votre isolement.""fr"
@@ -710,8 +711,7 @@ Les symptômes suivants peuvent aussi apparaître :
"Activer le traçage de proximité"
-
- "Pour utiliser l'application, les notifications d'exposition au COVID 19 doivent être activées."
+ "Pour que l’application puisse détecter des rencontres, la fonction de traçage doit être activée.""Activer"
@@ -743,7 +743,7 @@ Les symptômes suivants peuvent aussi apparaître :
"Voulez-vous vraiment supprimer la notification ? ""Ignorer l'optimisation de la batterie"
- "Pour pouvoir détecter les contacts en tout temps, désactiver l'optimisation de la batterie pour cette application."
+ "L’option d’optimisation de la batterie doit être désactivée pour que l’application fonctionne.""Autoriser""OK""Seulement pour cette application"
@@ -766,8 +766,7 @@ Les symptômes suivants peuvent aussi apparaître :
"Notifications d'exposition au COVID 19 désactivé"
-
- "Activez les notifications d'exposition au COVID 19 pour que le traçage de proximité fonctionne."
+ "Il faut activer {TRACING_SETTING_TEXT} pour que le traçage anonyme fonctionne.""Le message n'a pas pu être envoyé. Veuillez autoriser le partage de vos IDs aléatoires avec l'application SwissCovid."
@@ -811,10 +810,8 @@ Si aucune mise à jour n'est actuellement disponible sur votre appareil, veuille
"SwissCovid reste active, même lorsque l'application est fermée."
- "SwissCovid
-stoppe les chaînes
-de transmission."
- "Même si aucune donnée personnelle n'est envoyée sur vous, il est possible que quelqu'un se souvienne du contact sur la base de la date."
+ "SwissCovid – information rapide en cas de risque d’infection."
+ "Même si aucune donnée personnelle n’est transmise à votre sujet, il se peut que, grâce à la date, un utilisateur se souvienne du contact.""Est-ce que je reste anonyme ?"
@@ -824,7 +821,7 @@ de transmission."
"Notifications d'exposition au COVID 19"
- "Pour utiliser l'application, les notifications d'exposition au COVID 19 doivent être activées."
+ "Pour que l’application puisse détecter des contacts, la fonction de traçage doit être activée.""SwissCovid"
@@ -917,7 +914,7 @@ Schwarzenburgstrasse 157
"Voyages"
- "SwissCovid fonctionne dans les pays suivants :"
+ "SwissCovid fonctionne dans les pays suivants :""Qu'est-ce que cela signifie ?"
@@ -1261,7 +1258,9 @@ d'ouvrir un site internet."
"Terminer"
- "Aucun contact n'est enregistré. Le traçage peut être réactivé après l'isolement."
+ "Plus aucun nouveau contact n’est enregistré et il n’est plus possible de se reconnecter.
+
+Les deux fonctions peuvent être réactivées à la fin de votre isolement.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1293,9 +1292,9 @@ d'ouvrir un site internet."
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Vous êtes sourd ou malentendant et vous ne pouvez pas appeler l'infoline dédiée au coronavirus ?
+ "Vous êtes sourd ou malentendant et vous ne pouvez pas appeler l’infoline ?
-Alors envoyez un message à covid-support@medgate.ch."
+Alors, envoyez un courriel à covid-support@medgate.ch""Codes COVID"
@@ -1348,8 +1347,8 @@ La vitesse est un facteur décisif dans la lutte contre le coronavirus : moins o
"Le graphique indique les nouvelles infections déclarées pendant les 28 derniers jours pour donner un aperçu de l'évolution actuelle."
- "Vous avez été testé positif (test PCR ou test rapide antigénique) mais vous n'avez pas encore reçu de code COVID après 4 heures ?
-Contactez l'infoline coronavirus :"
+ "Le résultat de votre test (PCR ou antigénique) est positif, mais vous n’avez pas encore reçu de code COVID après 4 heures ?
+Appelez :""Pas encore de code COVID ?"
@@ -1373,7 +1372,7 @@ Contactez l'infoline coronavirus :"
"Pays compatibles :"
- "Afin que les voyageurs en provenance de l'étranger puissent aussi être avertis, les clés privées sont partagées avec les applications des pays compatibles."
+ "Afin que les voyageurs en provenance de l’étranger puissent aussi être avertis, les clés privées sont partagées avec les applications des pays compatibles.""Total"
@@ -1388,13 +1387,13 @@ Contactez l'infoline coronavirus :"
"Qui est averti ?"
- "Tous les contacts recensés pendant votre phase contagieuse du {ONSET_DATE} jusqu'à la saisie de votre code COVID sont avertis en cas de risque d'infection dû à la distance et à la durée du contact."
+ "Tous les contacts recensés pendant votre phase contagieuse du {ONSET_DATE} jusqu'à la saisie de votre code COVID sont avertis en cas de risque d'infection dû à la distance et à la durée du contact ou si vous avez été connecté au même endroit qu’un autre utilisateur.""{ONSET_DATE} – aujourd'hui"
- "Merci de nous aider à briser les chaînes d'infection."
+ "Vous contribuez ainsi à avertir d’autres utilisateurs d’une possible infection.""Les clés privées de votre application ont été envoyées pour la période suivante :"
@@ -1433,365 +1432,357 @@ Contactez l'infoline coronavirus :"
"Dans 1 jour"
-
+ "Rappel de déconnexion"
-
+ "Souhaitez-vous vous déconnecter ?"
-
+ "Déconnexion automatique"
-
+ "Il y a eu une déconnexion automatique."
-
+ "Scanner le code QR et se connecter"
-
+ "Code QR invalide !"
-
+ "De"
-
+ "À"
-
+ "Ajouter une mention"
-
+ "P. ex. qui vous a accompagné ?"
-
+ "Fin"
-
+ "Rappel de déconnexion"
-
+ "Arrêter"
-
+ "Veuillez vous authentifier pour accéder au journal"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "L'application doit avoir accès à l'appareil photo pour pouvoir scanner le code QR."
-
+ "Mes connexions"
-
+ "Connexion"
-
+ "L’appareil photo est nécessaire pour scanner le code QR."
-
+ "Autorisez la reconnaissance faciale pour accéder à votre journal en toute sécurité."
-
+ "Veuillez vous authentifier pour accéder au journal"
-
+ "Déconnexion"
-
+ "Connexion"
-
+ "Connexion"
-
+ "Créer un code QR"
-
+ "Proposer des connexions ?"
-
+ "Se connecter "
-
+ "Le rappel n’a pas pu être activé."
-
+ "Afin de recevoir un rappel de déconnexion, vous devez autoriser la fonction \\"Notifications\\" dans les paramètres de l’application."
-
+ "Adapter"
-
+ "Traiter"
-
+ "Il n'existe aucune donnée."
-
+ "Si vous scannez un code QR SwissCovid pour vous connecter sur place, une donnée est automatiquement enregistrée."
-
+ "SwissCovid doit avoir accès à l’appareil photo de votre smartphone. L’appareil photo est uniquement utilisé pour scanner les codes QR."
-
+ "Autoriser l’accès à l’appareil photo"
-
+ "P. ex. lieu de rencontre ou nom de l’événement"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Imprimer le PDF"
+ "Information"
+ "Problème de réseau"
+ "Veuillez vérifier votre connexion Internet."
+ "Essayez à nouveau"
+ "Notifications désactivées"
+ "Veuillez modifier vos paramètres afin de recevoir directement les notifications."
+ "Modifier les paramètres"
+ "Pas d’accès à l’appareil photo"
+ "L'application doit avoir accès à l'appareil photo pour pouvoir scanner le code QR."
+ "Erreur"
+ "Le code QR n’est pas encore valable."
+ "Le code QR n’est plus valable."
+ "Votre connexion a déjà été établie."
+ "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 "
+ "Déconnecter"
+ "Fonction de rappel"
+ "Notification de connexion permanente"
+ "Connexion activée depuis {TIME} heures."
-
+ "Sauvegarder"
-
+ "Scanner le code QR"
-
+ "Connexion désactivée"
-
+ "Pas de connexion possible actuellement"
-
+ "Scannez un code QR SwissCovid pour vous connecter sur place."
-
+ "Si vous scannez un code QR SwissCovid pour vous connecter sur place, une donnée est automatiquement enregistrée."
-
-
-
+ "Connexion effectuée"
+ "Du {DATE1} au {DATE2}"
+ "Plus d’informations sur le code COVID"
-
+ "Effacer la donnée enregistrée"
-
+ "Effacer"
-
+ "Partager"
-
+ "Imprimer"
-
+ "Scannez un code QR SwissCovid pour vous connecter sur place."
-
+ "Partager les données de connexion"
-
+ "Si vous partagez vos données de connexion, l’application peut notifier les personnes qui étaient connectées au même endroit et à la même heure."
-
-
-
-
-
-
+ "Sélectionner tous"
+ "Ne pas transmettre"
+ "Ne pas partager"
+ "Ne voulez-vous vraiment pas partager les données ?"
+ "Le partage des clés privées permet à SwissCovid de notifier des personnes ayant pu s’infecter et de freiner ainsi la propagation du virus. Si vous ne partagez pas vos clés privées, des personnes ayant potentiellement été infectées ne peuvent pas être averties."
-
+ "Effacer la donnée enregistrée"
-
+ "La donnée a été définitivement effacée de la liste."
-
+ "Effacer définitivement"
-
+ "Masquer uniquement et maintenir en vigueur les notifications ?"
-
+ "Test positif ?"
-
+ "Réactiver contacts et connexions."
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "Vous ne pouvez plus vous connecter après un test positif."
-
+ "Créez un code QR permettant aux utilisateurs de se connecter sur place."
-
+ "Souhaitez-vous vraiment supprimer le code QR ?"
-
+ "Auto-connexion"
-
+ "Scannez un code QR SwissCovid pour vous connecter sur place."
-
+ "Sauvegarde impossible"
-
+ "Les heures sélectionnées coïncident avec d’autres connexions dans votre journal. Vérifiez encore une fois votre saisie."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "Les données de connexion sélectionnées ont été transmises. Toutes les personnes qui étaient connectées au même endroit à la même heure seront notifiées."
-
+ "Connexion SwissCovid"
-
+ "Se connecter sans enregistrer de données"
-
+ "Avec l’application SwissCovid, vous pouvez facilement vous connecter, sans enregistrer vos données. L’application vous informera rapidement d’un éventuel risque d’infection."
-
+ "Application SwissCovid"
-
+ "Télécharger"
-
+ "Se connecter sans enregistrer de données"
-
+ "Saisissez ici votre code COVID pour alerter d’autres utilisateurs."
-
+ "Fin de l’isolement ?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Souhaitez-vous proposer des connexions ?"
+ "À quoi sert la fonction de connexion ?"
+ "La fonction de connexion sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment."
+ "Pas de sauvegarde centralisée des données"
+ "La fonction de connexion SwissCovid ne nécessite pas la saisie de coordonnées comme le nom, l’adresse ou le numéro de téléphone et elle n’enregistre pas les données de géolocalisation. La liste des connexions est enregistrée localement sur votre smartphone. Les données locales sont effacées après 14 jours."
+ "Créer un code QR"
+ "Au moyen du code QR SwissCovid, il est possible de se connecter sur place sans devoir enregistrer ses données."
+ "Cette fonction ne remplace pas la collecte obligatoire des coordonnées."
+ "La connexion SwissCovid ne remplace la collecte des coordonnées prescrite par l’« ordonnance COVID-19 situation particulière »."
+ "Où la fonction de connexion peut-elle être utilisée ?"
+ "À quoi sert la fonction de connexion ?"
+ "La fonction de connexion SwissCovid sera utile lors de rencontres dans des lieux où la collecte des coordonnées n’est pas obligatoire. Il peut s’agir – de rencontres privées – d’événements organisés par des associations – de rassemblements dans le cadre professionnel, p. ex. salles de réunion, bureaux, cantines, etc."
+ "La fonction de connexion sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment."
-
+ "Situations à risque"
-
+ "Contacts"
-
+ "Connexion"
-
+ "Que dois-je faire ?"
-
+ "Connexion positive"
-
+ "Connexions / contacts positifs"
-
+ "Code QR de l’utilisateur"
-
+ "Code QR de traçage des contacts"
-
+ "Après un test positif, la fonction de connexion est automatiquement désactivée. Elle peut être réactivée après la fin de l’isolement."
-
-
-
-
-
-
-
-
+ "Saisissez un titre qui décrit le lieu ou l’événement. Ce titre s’affichera lors de la connexion."
+ "Que pouvez-vous faire ?"
+ "Protéger les contacts"
+ "Observer les symptômes"
+ "Se faire tester"
+ "Il est possible d’être contagieux sans le savoir. Respectez les règles d’hygiène et de conduite pour protéger votre famille, vos amis et tout votre entourage."
+ "Surveillez votre état de santé."
+ "Faites-vous immédiatement tester si vous présentez des symptômes. Même en l’absence de symptômes il peut être utile et recommandé de se faire tester pour le coronavirus."
-
+ "Connexion"
-
-
-
-
-
+ "Pas de données transmises"
+ "Aucune clé privée n’a été transmise."
+ "Retour"
+ "Ne pas transmettre de données"
+ "Aucune donnée de connexion n’a été transmise."
-
-
-
-
-
-
-
-
+ "Partager la sélection"
+ "Ne rien partager"
+ "Se connecter sans enregistrer de données"
+ "Connexions"
+ "La fonction de connexion SwissCovid vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre."
+ "Votre présence est seulement enregistrée dans votre application. Il n’est pas nécessaire de saisir vos coordonnées dans l’application."
+ "La fonction de connexion SwissCovid vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre. Votre présence est seulement enregistrée dans votre application. Il n’est pas nécessaire de saisir vos coordonnées dans l’application. À cette fin, les conditions d’utilisation et la déclaration de protection des données ont été mises à jour."
+ "Passer à l'étape suivante "
-
+ "La donnée codée est enregistrée en arrière-plan dans l’application pendant 14 jours. Ainsi, en cas de possible infection, une notification peut vous être envoyée."
-
-
+ "Masquer uniquement"
+ "L’application peut vous rappeler de vous déconnecter au moment voulu."
-
+ "Ou entièrement effacer ?"
-
-
+ "La donnée est entièrement effacée. Vous ne pourrez plus recevoir d’avertissement."
+ "Personne ne sera notifié."
-
-
-
-
-
-
-
+ "Que dois-je savoir au sujet des tests ?"
+ "Installer l’application et se connecter"
+ "Complétez la configuration de l’application pour pouvoir utiliser la fonction de traçage."
+ "Terminer "
+ "Configuration terminée"
+ "Merci d’aider à endiguer la propagation du coronavirus."
+ "Information rapide en cas de risque d’infection."
+
+
+
+ "https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 41c864c31..68c76ae3d 100644
--- a/common/src/main/res/values-hr/strings.xml
+++ b/common/src/main/res/values-hr/strings.xml
@@ -1403,365 +1403,363 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 7c27d5c1e..82a4ebdd0 100644
--- a/common/src/main/res/values-it/strings.xml
+++ b/common/src/main/res/values-it/strings.xml
@@ -104,7 +104,7 @@ Così contribuisci a fermare le catene di infezione."
"Possibile contagio"
- "Vi è la possibilità di un contagio."
+ "Potresti essere stato contagiato""App attiva"
@@ -194,7 +194,7 @@ Così contribuisci a fermare le catene di infezione."
"Con il codice Covid..."
- "Fermare le catene di infezione"
+ "Avvisa di possibili contagi""Immetti il codice Covid"
@@ -228,7 +228,7 @@ Così contribuisci a fermare le catene di infezione."
"Tracciamento disattivato""Attiva il tracciamento per far funzionare l'app.""Bluetooth disattivato"
- "Attiva Bluetooth sul tuo dispositivo per far funzionare il tracciamento."
+ "Attiva il Bluetooth sul tuo dispositivo per avviare il tracciamento anonimo""Attiva Bluetooth""Uso di Bluetooth negato""Consenti l'uso di Bluetooth sul tuo dispositivo per far funzionare il tracciamento."
@@ -260,11 +260,11 @@ Così contribuisci a fermare le catene di infezione."
"Segui le istruzioni sull'isolamento che hai ricevuto."
- "Tracciamento terminato"
- "Gli incontri non vengono più salvati."
+ "Incontri e check-in disattivati"
+ "Gli incontri non vengono salvati""Contatta la""Segui le"
- "istruzioni sull'auto-isolamento"
+ "Istruzioni sull'isolamento""Nuova segnalazione"
@@ -274,7 +274,7 @@ Così contribuisci a fermare le catene di infezione."
"Positivo al test"
- "Vi è la possibilità di un contagio."
+ "Potresti essere stato contagiato""Sei risultato positivo al test del coronavirus."
@@ -345,7 +345,7 @@ Infoline SwissCovid
"Gli ID casuali salvati restano sul tuo telefono cellulare e vengono eliminati dopo 14 giorni."
- "Come funziona l'app?"
+ "Incontri""Riconosce gli incontri
@@ -398,7 +398,7 @@ possibile contagio""Consenti messaggi"
- "Per poter essere informato dall'app, devi consentire i messaggi."
+ "Per poter essere informato dall'app, devi consentire le notifiche.""Consenti messaggi"
@@ -421,7 +421,7 @@ Segui le istruzioni sull'isolamento che hai ricevuto."
"L'app è installata"
- "Grazie per aiutarci a interrompere le catene di infezione."
+ "Grazie per aiutarci a contenere la diffusione del coronavirus.""Avvia"
@@ -448,7 +448,7 @@ Segui le istruzioni sull'isolamento che hai ricevuto."
"Possibile contagio"
- "Vi è il sospetto di un possibile contagio."
+ "Potresti essere stato contagiato""Errore nell'impostazione dell'ora"
@@ -588,9 +588,9 @@ Il consumo della batteria aumenta soltanto leggermente."
"Cosa sono le segnalazioni?"
- "L'app controlla regolarmente se altri utenti che hai incontrato hanno segnalato di essere positivi al test.
+ "L'app controlla regolarmente se altri utenti che hai incontrato o che hanno fatto il check-in nello stesso luogo hanno segnalato un risultato positivo del test.
-In caso di possibile contagio viene visualizzato un messaggio sulla schermata di blocco."
+In caso di possibile contagio viene visualizzata una notifica sulla schermata di blocco.""La Infoline SwissCovid ti offre consulenza sulla possibilità di fare un test gratuito."
@@ -629,9 +629,9 @@ In questo modo si garantisce che l'app segnali soltanto i casi confermati.""Che cosa viene inviato?"
- "Vengono inviate soltanto le chiavi private della tua app, non i tuoi dati personali.
+ "Vengono inviate soltanto le chiavi private dell'app e gli ID criptati dei tuoi check-in, non i tuoi dati personali.
-In questo modo gli altri utenti dell'app SwissCovid vengono a sapere che vi è la possibilità di un contagio."
+Gli altri utenti dell'app SwissCovid saranno così informati di un possibile contagio.""+41 58 387 77 78"
@@ -640,7 +640,7 @@ In questo modo gli altri utenti dell'app SwissCovid vengono a sapere che vi è l
"https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/it/categories/app-swisscovid"
- "Avvertenza importante"
+ "Informazione sull'immissione""Anche se non vengono inviati dati personali, è possibile che qualcuno possa ricordarsi dell'incontro sulla base della data in cui è avvenuto."
@@ -675,7 +675,7 @@ Possono inoltre comparire i seguenti sintomi:
"Dopo un test positivo, il tracciamento dell'app
-viene disattivato automaticamente. Il tracciamento può essere riattivato non appena avrete concluso l'isolamento"
+viene disattivato automaticamente. Il tracciamento può essere riattivato al termine dell'isolamento."
"it"
@@ -716,8 +716,7 @@ viene disattivato automaticamente. Il tracciamento può essere riattivato non ap
"Attivare il tracciamento"
-
- "Per utilizzare l'app è necessario attivare le notifiche di esposizione al COVID-19."
+ "Per riconoscere gli incontri è necessario attivare la funzione di tracciamento.""Attiva"
@@ -749,7 +748,7 @@ viene disattivato automaticamente. Il tracciamento può essere riattivato non ap
"Eliminare la segnalazione?""Ignora l'ottimizzazione della batteria"
- "Per poter riconoscere sempre gli incontri, l'ottimizzazione della batteria deve essere disattivata per questa app."
+ "Per utilizzare l'app è necessario disattivare l'ottimizzazione della batteria.""Consenti""OK""Solo per questa app"
@@ -772,8 +771,7 @@ viene disattivato automaticamente. Il tracciamento può essere riattivato non ap
"Notifiche di esposizione al COVID-19 disattivato"
-
- "Attiva le notifiche di esposizione al COVID-19 per avviare il tracciamento."
+ "Attiva {TRACING_SETTING_TEXT} per avviare il tracciamento anonimo.""Impossibile inviare la segnalazione. Consentire la condivisione dei propri ID casuali con l'app SwissCovid."
@@ -817,10 +815,9 @@ Se al momento non è disponibile alcun aggiornamento sul tuo dispositivo, riprov
"SwissCovid resta attiva anche quando l'app viene chiusa."
- "SwissCovid
-ferma le
-catene di infezione"
- "Anche se non vengono inviati dati personali, è possibile che qualcuno possa ricordarsi dell'incontro sulla base della data in cui è avvenuto."
+ "SwissCovid –
+informazione rapida in caso di rischio di contagio."
+ "Sì, ma anche se non vengono inviati dati personali, è possibile che qualcuno possa ricordarsi dell'incontro sulla base della data in cui è avvenuto.""Resto anonimo?"
@@ -830,7 +827,7 @@ catene di infezione"
"Notifiche di esposizione al COVID-19"
- "Per utilizzare l'app è necessario attivare le notifiche di esposizione al COVID-19."
+ "Per riconoscere gli incontri è necessario attivare la funzione di tracciamento.""SwissCovid"
@@ -1267,9 +1264,9 @@ Affinché anche i viaggiatori all'estero possano essere avvisati, la condivision
"Interrompi"
- "Non verranno più registrati i contatti.
+ "Gli incontri non vengono più salvati e non sono più possibili nuovi check-in.
- Il tracciamento può essere riattivato non appena avrete concluso l'isolamento"
+Entrambe le funzioni potranno essere riattivate al termine dell'isolamento.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1301,9 +1298,9 @@ Affinché anche i viaggiatori all'estero possano essere avvisati, la condivision
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Siete sordi o audiolesi e non potete chiamare l'Infoline Coronavirus?
+ "Sei sordo o audioleso e non puoi telefonare?
-Scrivete un'e-mail all'indirizzo: covid-support@medgate.ch"
+Scrivi un'e-mail all'indirizzo: covid-support@medgate.ch""Codici Covid"
@@ -1357,7 +1354,7 @@ Nella lotta al coronavirus il tempo è un fattore fondamentale: meno tempo trasc
"Sei risultato positivo al test (PCR o antigenico rapido) e dopo quattro ore non hai ancora ricevuto un codice Covid?
-Contatta la Infoline coronavirus:"
+Chiama il seguente numero:"
"Non hai ancora un codice Covid?"
@@ -1381,7 +1378,7 @@ Contatta la Infoline coronavirus:"
"Paesi compatibili:"
- "Affinché anche i viaggiatori all'estero possano essere avvisati, le chiavi private vengono condivise con le app di tracciamento dei Paesi compatibili."
+ "Affinché anche i viaggiatori provenienti dall'estero possano essere avvisati dopo un incontro, le chiavi private vengono condivise con le app di tracciamento dei Paesi compatibili.""Totale"
@@ -1396,13 +1393,13 @@ Contatta la Infoline coronavirus:"
"Chi viene avvertito?"
- "Vengono avvertiti tutti i contatti nel periodo della tua fase contagiosa dal {ONSET_DATE} fino all'immissione del codice Covid, se in base alla distanza e alla durata vi è stata la possibilità di un contagio."
+ "Vengono avvisati tutti i contatti durante la tua fase contagiosa dal {ONSET_DATE} fino all'immissione del codice Covid, se possono essere stati contagiati in base alla distanza e alla durata o hanno fatto il check-in nello stesso luogo.""{ONSET_DATE} – oggi"
- "Così contribuisci a fermare le catene di infezione."
+ "Così contribuisci ad avvisare gli altri di un possibile contagio.""Le chiavi private della tua app sono state inviate per il seguente periodo:"
@@ -1441,365 +1438,363 @@ Contatta la Infoline coronavirus:"
"Fra 1 giorno"
-
+ "Promemoria check-out"
-
+ "Vuoi fare il check-out?"
-
+ "Check-out automatico"
-
+ "È stato effettuato il check-out automatico."
-
+ "Scansiona il codice QR e fai il check-in"
-
+ "Codice QR non valido!"
-
+ "Dal"
-
+ "Al"
-
+ "Aggiungi una nota"
-
+ "p. es. con chi eri?"
-
+ "Fatto"
-
+ "Promemoria check-out"
-
+ "Disattiva"
-
+ "Autenticati per accedere al diario"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "L'app deve avere accesso alla fotocamera per poter scansionare il codice QR."
-
+ "I miei check-in"
-
+ "Check-in"
-
+ "Per scansionare il codice QR è necessaria la fotocamera."
-
+ "Consenti l'utilizzo di Face-ID per accedere al diario in modo sicuro."
-
+ "Autenticati per accedere al diario"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Crea codice QR"
-
+ "Offrire i check-in?"
-
+ "Fai il check-in ora"
-
+ "Impossibile impostare il promemoria"
-
+ "Per ricevere i promemoria check-out devi consentire le \\"notifiche\\" nelle impostazioni dell'app."
-
+ "Adatta"
-
+ "Modifica"
-
+ "Ancora nessuna voce presente"
-
+ "Quando scansioni un codice QR SwissCovid per fare il check-in sul posto, viene creata automaticamente una voce qui."
-
+ "SwissCovid necessita dell'accesso alla fotocamera del tuo dispositivo. La fotocamera è utilizzata solo per scansionare i codici QR."
-
+ "Consenti l'accesso alla fotocamera"
-
+ "p. es. locale o nome dell'evento"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Stampa PDF"
+ "Info"
+ "Errore di rete"
+ "Verifica la connessione a Internet."
+ "Riprova"
+ "Notifiche disattivate"
+ "Modifica le impostazioni per essere informato direttamente in caso di segnalazione."
+ "Modifica le impostazioni"
+ "Nessun accesso alla fotocamera"
+ "L'app necessita dell'accesso alla fotocamera per poter scansionare il codice QR."
+ "Errore"
+ "Codice QR non ancora valido"
+ "Codice QR non più valido"
+ "Hai già fatto il check-in."
+ "Questo codice QR non è compatibile con la versione dell'app installata. Aggiorna l'app per continuare."
+ "Aggiornamento necessario"
+ "Aggiorna ora"
+ "Check-out"
+ "Posponi"
+ "Notifica check-in permanente"
+ "Hai fatto il check-in alle {TIME}."
-
+ "Salva"
-
+ "Scansiona codice QR"
-
+ "Check-in disattivato"
-
+ "Impossibile fare il check-in"
-
+ "Scansiona un codice QR SwissCovid per fare il check-in."
-
+ "Quando scansioni un codice QR SwissCovid per fare il check-in sul posto, viene creata automaticamente una voce qui."
-
-
-
+ "Hai fatto il check-in"
+ "Dal {DATE1} al {DATE2}"
+ "Maggiori informazioni sul codice Covid"
-
+ "Rimuovi voce"
-
+ "Elimina"
-
+ "Condividi"
-
+ "Stampa"
-
+ "Scansiona un codice QR SwissCovid per fare il check-in sul posto."
-
+ "Condividi le voci di check-in"
-
+ "Se condividi le tue voci di check-in, l'app potrà informare le persone che hanno fatto il check-in nello stesso luogo e nello stesso momento."
-
-
-
-
-
-
+ "Seleziona tutto"
+ "Non inviare"
+ "Non condividere"
+ "Non condividere?"
+ "La condivisione delle tue chiavi private aiuterà «SwissCovid» ad avvisare altre persone potenzialmente infette e a prevenire l'ulteriore diffusione del virus. Se non condividi le tue chiavi private, non sarà avvisata nessuna persona potenzialmente infetta."
-
+ "Elimina voce"
-
+ "La tua voce sarà eliminata definitivamente dall'elenco."
-
+ "Elimina definitivamente"
-
+ "Nascondi e continua a ricevere notifiche?"
-
+ "Positivo al test?"
-
+ "Riattiva gli incontri e il check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "Se sei risultato positivo al test, non puoi più fare il check-in."
-
+ "Crea un codice QR per consentire alle persone di fare il check-in sul posto"
-
+ "Eliminare il codice QR?"
-
+ "Self check-in"
-
+ "Scansiona un codice QR SwissCovid per fare il check-in sul posto."
-
+ "Impossibile salvare"
-
+ "Gli orari selezionati si sovrappongono ad altri check-in del diario. Verifica nuovamente i dati immessi."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "I check-in selezionati sono stati inviati. Saranno avvisate tutte le persone che hanno fatto il check nello stesso luogo e nello stesso momento."
-
+ "Check-in SwissCovid"
-
+ "Fai il check-in senza lasciare dati"
-
+ "Da noi con l'app SwissCovid puoi fare il check-in facilmente, senza lasciare dati. In caso di rischio di contagio sarai informato rapidamente dall'app."
-
+ "App SwissCovid"
-
+ "Scarica ora"
-
+ "Check-in
+senza lasciare dati"
-
+ "Immetti il codice Covid per avvisare gli altri"
-
+ "Terminare l'isolamento?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Vuoi offrire i check-in?"
+ "Perché una funzione check-in?"
+ "La funzione check-in senza registrazione dei dati di contatto completa l'app SwissCovid. Con questa funzione, in caso di test positivo puoi avvisare non solo i contatti stretti, ma anche le persone che hanno fatto il check-in nello stesso luogo e nello stesso momento."
+ "Nessun salvataggio centralizzato dei dati"
+ "Per il check-in SwissCovid non occorre fornire dati personali come il nome, l'indirizzo o il numero di telefono e non sono registrati dati sulla posizione GPS. L'elenco dei check-in è salvato localmente sul tuo smartphone. Dopo 14 giorni, i dati locali sono cancellati."
+ "Crea un codice QR"
+ "Il codice QR SwissCovid consente di fare il check-in sul posto senza lasciare dati."
+ "Nessuna esenzione dall'obbligo di registrare i dati di contatto"
+ "Il check-in SwissCovid non esenta dall'obbligo di registrare i dati di contatto secondo l'«ordinanza COVID-19 situazione particolare»."
+ "Dove può essere impiegato il check-in?"
+ "Perché una funzione check-in?"
+ "Il check-in SwissCovid può essere impiegato dove più persone s'incontrano, ma non vige alcun obbligo di registrare i dati di contatto.
+
+Possibili luoghi d'impiego:
+– eventi privati
+– eventi associativi
+– incontri in ambito professionale, p. es. in sale riunioni, uffici, mense ecc."
+ "La funzione check-in senza registrazione dei dati di contatto completa l'app SwissCovid. Con questa funzione, in caso di test positivo puoi avvisare non solo i contatti stretti, ma anche le persone che hanno fatto il check-in nello stesso luogo e nello stesso momento."
-
+ "Situazione a rischio"
-
+ "Incontri"
-
+ "Check-in"
-
+ "Cosa devo fare?"
-
+ "Check-in positivo"
-
+ "Check-in/contatti positivi"
-
+ "Codice QR utente"
-
+ "Codice QR tracciamento dei contatti"
-
+ "Dopo un test positivo, la funzione check-in viene disattivata automaticamente. Può essere riattivata al termine dell'isolamento."
-
-
-
-
-
-
-
-
+ "Attribuisci un titolo che descriva il luogo o l'evento. Questo titolo sarà visualizzato al momento del check-in."
+ "Cosa puoi fare?"
+ "Proteggi i tuoi contatti"
+ "Presta attenzione ai sintomi"
+ "Fai il test"
+ "Potresti già essere contagioso senza accorgertene. Rispetta le misure d'igiene e di protezione vigenti e proteggi la tua famiglia, i tuoi amici e il tuo ambiente."
+ "Sorveglia il tuo stato di salute."
+ "In caso di sintomi fai subito il test. Il test è raccomandato anche in assenza di stintomi."
-
+ "Check-in"
-
-
-
-
-
+ "Nessun dato inviato"
+ "Nessuna chiave privata inviata"
+ "Indietro"
+ "Non inviare nessun dato"
+ "Nessuna voce di check-in inviata"
-
-
-
-
-
-
-
-
+ "Condividi la selezione"
+ "Non condividere"
+ "Check-in senza lasciare dati"
+ "Check-in"
+ "Con i check-in SwissCovid puoi fare il check-in in un luogo o a un incontro con l'app."
+ "La tua presenza è salvata solo nell'app. L'app non chiede dati personali."
+ "Con i check-in SwissCovid puoi fare il check-in in un luogo o a un incontro con l'app. La tua presenza è salvata solo nell'app. L'app non chiede dati personali. Le condizioni per l'uso e l'informativa sulla protezione dei dati sono state aggiornate."
+ "Ignora"
-
+ "La voce è salvata dall'app in background in forma criptata per 14 giorni. Così potrai essere avvisato comunque in caso di possibile contagio."
-
-
+ "Nascondi soltanto"
+ "L'app può ricordarti di fare il check-out al momento giusto."
-
+ "O elimina definitivamente?"
-
-
+ "La voce sarà eliminata definitivamente. Non potrai più essere avvisato."
+ "Non sarà informato nessuno."
-
-
-
-
-
-
-
+ "Cosa devo sapere a proposito del test?"
+ "Installa l'app e fai il check-in"
+ "Per poter utilizzare la funzione di tracciamento devi completare la configurazione dell'app."
+ "Completa ora"
+ "Configurazione completata"
+ "Grazie per aiutarci a contenere la diffusione del coronavirus."
+ "Informazione rapida in caso di rischio di contagio"
+
+
+
+ "https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 6ee0bfebe..e2a649b86 100644
--- a/common/src/main/res/values-pt/strings.xml
+++ b/common/src/main/res/values-pt/strings.xml
@@ -1408,365 +1408,363 @@ Contacte a linha de informação coronavírus:"
"Em 1 dia"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 9b116c6e1..4f92d7d88 100644
--- a/common/src/main/res/values-rm/strings.xml
+++ b/common/src/main/res/values-rm/strings.xml
@@ -1403,365 +1403,363 @@ Alura contactai la Infoline coronavirus:"
"En 1 di"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 4af0a8abe..3ecbc1323 100644
--- a/common/src/main/res/values-sq/strings.xml
+++ b/common/src/main/res/values-sq/strings.xml
@@ -1408,365 +1408,363 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"Në 1 ditë"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 112e8d072..ad4d2cdca 100644
--- a/common/src/main/res/values-sr/strings.xml
+++ b/common/src/main/res/values-sr/strings.xml
@@ -1403,365 +1403,363 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 561dbb8bf..4a072b544 100644
--- a/common/src/main/res/values-ti/strings.xml
+++ b/common/src/main/res/values-ti/strings.xml
@@ -1385,365 +1385,363 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ኣብ 1 መዓልቲ"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 deb5dcc17..bb6dff3e3 100644
--- a/common/src/main/res/values-tr/strings.xml
+++ b/common/src/main/res/values-tr/strings.xml
@@ -1385,365 +1385,363 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"1 gün sonra"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ 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 18eaafde7..a97154e9b 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -104,7 +104,7 @@ Thank you for helping to break the chain of infection."
"Possible infection"
- "An infection could have occurred"
+ "You may have been infected""App active"
@@ -194,7 +194,7 @@ Thank you for helping to break the chain of infection."
"With the Covidcode"
- "Break the chains of infection"
+ "Alert the app to possible infections""Enter the Covidcode"
@@ -228,7 +228,7 @@ Thank you for helping to break the chain of infection."
"Proximity tracing deactivated""Activate proximity tracing so that the app works.""Bluetooth off"
- "Turn on Bluetooth on your device so that proximity tracing works."
+ "Turn on Bluetooth on your device so that anonymous tracing works.""Turn on Bluetooth""Bluetooth use declined""Enable the use of Bluetooth so that proximity tracing works."
@@ -260,8 +260,8 @@ Thank you for helping to break the chain of infection."
"Follow the instructions you receive on isolation."
- "Tracing deactivated"
- "No further encounters registered"
+ "Encounters and check-in deactivated"
+ "No encounters registered""Contact the""Follow the""Instructions on self-isolation"
@@ -274,7 +274,7 @@ Thank you for helping to break the chain of infection."
"Tested positive"
- "An infection could have occurred"
+ "You may have been infected""You have tested positive for the coronavirus."
@@ -330,7 +330,7 @@ Infoline SwissCovid
"By using the SwissCovid app, we can all help to contain the spread of the coronavirus."
- "The app tells you if you might have been exposed to the coronavirus."
+ "The app alerts you if you might have been exposed to the coronavirus.""Privacy by design"
@@ -345,7 +345,7 @@ Infoline SwissCovid
"The random IDs are stored on your device for 14 days and then deleted."
- "How does the app work?"
+ "Encounters""Recognising encounters using Bluetooth"
@@ -363,7 +363,7 @@ Infoline SwissCovid
"Report of potential infection"
- "The app tells you if you might have been exposed to the coronavirus."
+ "The app alerts you if you might have been exposed to the coronavirus.""By responding appropriately, you can break the chain of infection and protect other people."
@@ -396,7 +396,7 @@ Infoline SwissCovid
"Allow messages"
- "In order to receive notifications from the app, you must enable messages."
+ "In order to receive alerts from the app, you must enable notifications.""Enable messages"
@@ -419,7 +419,7 @@ Follow the instructions you have received on isolation."
"The app is ready to go"
- "Thank you for helping to break the chain of infection."
+ "Thank you for helping to stop the spread of coronavirus.""Start"
@@ -446,7 +446,7 @@ Follow the instructions you have received on isolation."
"Possible infection"
- "An infection could have occurred"
+ "You may have been infected""Time error"
@@ -584,9 +584,9 @@ They are saved locally on the smartphones and then deleted 14 days later.""What are reports?"
- "The app checks regularly to see whether any users that you have encountered have reported testing positive for the virus.
+ "The app checks regularly to see whether any users that you have encountered or who were checked in at the same location as you have reported testing positive for the virus.
-If it is possible that you have been infected, you will receive a message on your lock screen."
+If it is possible that you have been infected, you will receive a notification on your lock screen.""The infoline SwissCovid will advise you on getting a free test."
@@ -626,9 +626,9 @@ This guarantees that only confirmed cases are reported via the app."
"What information gets sent?"
- "Only the private keys from your app are sent, no personal data.
+ "Only the private keys from your app and the encrypted IDs from your check-ins are sent, no personal data.
-Other SwissCovid apps can then check if it is possible that they have been infected."
+This tells other SwissCovid app users that they may have been infected.""+41 58 387 77 78"
@@ -637,7 +637,7 @@ Other SwissCovid apps can then check if it is possible that they have been infec
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/en/categories/swisscovid-app"
- "Important information"
+ "Information on the entry""Although no personal data relating to you is sent out, it may well be that someone remembers their encounter with you from the date."
@@ -711,8 +711,7 @@ Other symptoms may include:
"Activate proximity tracing"
-
- "COVID-19 Exposure Notifications must be activated to use the app."
+ "The tracing function must be activated to detect encounters.""Activate"
@@ -744,7 +743,7 @@ Other symptoms may include:
"Do you really want to delete the notification? ""Ignore battery optimisation"
- "To be able to recognise encounters at all times, the battery optimisation function for this app must be disabled."
+ "For the app to work, battery optimisation must be switched off.""Allow""OK""Only for this app"
@@ -767,8 +766,7 @@ Other symptoms may include:
"COVID-19 Exposure Notifications disabled"
-
- "Activate COVID-19 Exposure Notifications for proximity tracing to work."
+ "Activate {TRACING_SETTING_TEXT} for anonymous tracing to work.""The message could not be sent. Please allow your random IDs to be shared with the SwissCovid app."
@@ -810,10 +808,9 @@ If no update is currently available on your device, please try again later.""SwissCovid remains active even if the app has been closed."
- "SwissCovid
-breaks
-chains of infection."
- "Although no personal data relating to you is sent out, it may well be that someone remembers their encounter with you from the date."
+ "SwissCovid –
+find out fast if you're at risk of infection."
+ "Yes, but although no personal data is sent out, it may well be that someone remembers their encounter with you from the date.""Does anyone find out who I am?"
@@ -823,7 +820,7 @@ chains of infection."
"COVID-19 Exposure Notifications"
- "COVID-19 Exposure Notifications must be activated to use the app."
+ "The tracing function must be activated to detect encounters.""SwissCovid"
@@ -916,7 +913,7 @@ Schwarzenburgstrasse 157
"Travel"
- "SwissCovid works in the following countries:"
+ "The SwissCovid encounters function works in the following countries:""What does that mean?"
@@ -1256,9 +1253,9 @@ To enable travellers from abroad to also receive alerts, this happens regardless
"End"
- "Notifications will no longer be stored.
+ "No new encounters will be stored and no new check-ins are possible either.
-Tracing can be reactivated after isolation."
+Both functions can be reactivated when the isolation period is over.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1290,9 +1287,9 @@ Tracing can be reactivated after isolation."
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Are you deaf or hard of hearing and unable to call the coronavirus infoline on the phone?
+ "Are you deaf or hard of hearing and unable to call?
-Send an email to covid-support@medgate.ch."
+Send an email to covid-support@medgate.ch""Covidcodes"
@@ -1345,8 +1342,8 @@ Time is a crucial factor when it comes to combating coronavirus: the shorter the
"The chart shows new infections reported in the last 28 days. This gives an overview of current developments."
- "Have you tested positive (PCR test or rapid antigen test) and did not receive a Covidcode after 4 hours?
-If so, contact the Coronavirus Infoline:"
+ "Have you tested positive (PCR test or rapid antigen test) and were not sent a Covidcode after 4 hours?
+If so, call this number:""Don’t have a Covidcode yet?"
@@ -1385,13 +1382,13 @@ If so, contact the Coronavirus Infoline:"
"Who will be notified?"
- "All the people you came into contact with during the infectious period from {ONSET_DATE} until you entered the Covidcode will be notified, if there is a possibility of infection due to the proximity and duration of the contact."
+ "All the people you came into contact with during the infectious period from {ONSET_DATE} until you entered the Covidcode will be notified, if there is a possibility of infection due to the proximity and duration of the contact, or if you were checked-in at the same location.""{ONSET_DATE} – today"
- "In this way you are helping break chains of infection."
+ "In this way you are helping alert others to a possible infection.""Your app's private key was sent for the following period:"
@@ -1430,365 +1427,363 @@ If so, contact the Coronavirus Infoline:"
"In 1 day"
-
+ "Check-out reminder"
-
+ "Do you want to check out?"
-
+ "Automatic check-out"
-
+ "You were checked out automatically."
-
+ "Scan QR code and check in"
-
+ "No valid QR code!"
-
+ "From"
-
+ "To"
-
+ "Add note"
-
+ "e.g. who were you with?"
-
+ "Done"
-
+ "Check-out reminder"
-
+ "Off"
-
+ "Authentication needed to access the diary"
-
+ "{HOURS} h"
-
+ "{MINUTES}'"
-
+ "The app needs to access the camera to be able to scan the QR code."
-
+ "My check-ins"
-
+ "Check-in"
-
+ "The camera is needed to scan the QR code."
-
+ "Allow Face ID to securely access your diary."
-
+ "Authentication needed to access the diary"
-
+ "Check-out"
-
+ "Check-in"
-
+ "Check-in"
-
+ "Generate QR code"
-
+ "Offer check-ins?"
-
+ "Check in now"
-
+ "The reminder could not be set"
-
+ "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
-
+ "Customise"
-
+ "Edit"
-
+ "No entries available yet"
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
+ "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
-
+ "Allow access to camera"
-
+ "e.g. room or event name"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Print PDF"
+ "Info"
+ "Network error"
+ "Check your internet connection."
+ "Try again"
+ "Notifications deactivated"
+ "Modify your settings to be informed straight away if you receive a notification."
+ "Modify settings"
+ "No access to camera"
+ "The app needs to access the camera to be able to scan the QR code."
+ "Error"
+ "QR code not yet valid"
+ "QR code no longer valid"
+ "You are already checked in."
+ "This QR code is not compatible with the installed version of the app. Please update the app to continue."
+ "Update required"
+ "Update now"
+ "Check out"
+ "Snooze"
+ "Permanent check-in notification"
+ "You have been checked in since {TIME}."
-
+ "Save"
-
+ "Scan QR code"
-
+ "Check-in deactivated"
-
+ "Check-in is not possible at the moment"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
-
-
-
+ "You're checked in"
+ "{DATE1} to {DATE2}"
+ "More about the Covidcode"
-
+ "Remove entry"
-
+ "Delete"
-
+ "Share"
-
+ "Print"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Share check-in entries"
-
+ "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
-
-
-
-
-
-
+ "Select all"
+ "Don't send"
+ "Don't share"
+ "Are you sure you don't want to share?"
+ "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
-
+ "Remove entry"
-
+ "Your entry will be permanently removed from the list."
-
+ "Completely remove"
-
+ "Only hide and keep receiving notifications?"
-
+ "Tested positive?"
-
+ "Reactivate encounters and check-in"
-
+ "{HOURS} h {MINUTES}'"
- "You can no longer check-in when you tested positive. More text more text more text more text more text more text more"
+ "You can no longer check in if you have tested positive."
-
+ "Generate a QR code that people can use to check in on site."
-
+ "Are you sure you want to delete the QR code?"
-
+ "Self check-in"
-
+ "Scan a SwissCovid QR code to check in on site."
-
+ "Saving not possible"
-
+ "The times you selected clash with other check-ins in your diary. Please check your entry."
-
-
-
- "Are you sure?"
-
-
- "Sharing your random IDs helps “SwissCovid” to notify other potential infected people and avoid the further spreading of the pandemic. If you don’t share your random IDs no automatically recognized encounters can be notified. "
-
-
- "Try again"
-
-
- "Don't share"
+ "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
-
+ "SwissCovid check-in"
-
+ "Check in without leaving any data"
-
+ "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
-
+ "SwissCovid app"
-
+ "Download now"
-
+ "Check in without leaving
+data"
-
+ "Enter your Covidcode here to alert others."
-
+ "Isolation ended?"
-
-
-
-
-
-
-
-
-
-
-
-
-
+ "Do you want to offer check-ins?"
+ "Why a check-in function?"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "No central data storage"
+ "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
+ "Generate a QR code"
+ "Using a SwissCovid QR code, people can check in on site without leaving their data."
+ "No substitute for mandatory recording of contact details"
+ "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
+ "Where can check-ins be used?"
+ "Why a check-in function?"
+ "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
+
+Possible locations:
+– Private events
+– Club meetings or events
+– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
+ "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
-
+ "Risk situation"
-
+ "Encounters"
-
+ "Check-in"
-
+ "What should I do?"
-
+ "Positive check-in"
-
+ "Positive check-ins/contacts"
-
+ "User QR code"
-
+ "Contact tracing QR code"
-
+ "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
-
-
-
-
-
-
-
-
+ "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
+ "What can you do?"
+ "Protect your contacts"
+ "Look out for symptoms"
+ "Get tested"
+ "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
+ "Monitor your state of health."
+ "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
-
+ "Check-in"
-
-
-
-
-
+ "No data sent"
+ "No private key was sent."
+ "Back"
+ "Don't send data"
+ "No check-in entries were sent."
-
-
-
-
-
-
-
-
+ "Share selection"
+ "Don't share anything"
+ "Check in without leaving data"
+ "Check-ins"
+ "With SwissCovid check-ins you can check in to a location or meeting using the app."
+ "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
+ "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
+ "Skip"
-
+ "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
-
-
+ "Hide only"
+ "The app can remind you to check out at the right time."
-
+ "Or remove completely?"
-
-
+ "The entry was removed completely. You can no longer be alerted."
+ "Nobody will be notified."
-
-
-
-
-
-
-
+ "What do I need to know about testing?"
+ "Install app and check in"
+ "Complete the app configuration to be able to use the tracing function."
+ "Complete now"
+ "Configuration complete"
+ "Thank you for helping stop the spread of coronavirus."
+ "Find out fast if you're at risk of infection."
+
+
+
+ "https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
+
\ No newline at end of file
From 064bf2ba1af53479af6252207bd481a3572e0ed5 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 31 May 2021 17:15:58 +0200
Subject: [PATCH 119/207] Make Master Public Key a BuildConfig field
---
app/build.gradle | 4 ++++
.../admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt | 5 +----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 49cbc2c61..f13efd81f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -71,6 +71,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt-d.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-d.swisscovid.ch"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.dev.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.dev.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.dev'
@@ -85,6 +86,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-a.swisscovid.ch"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.abnahme.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.abnahme'
@@ -99,6 +101,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
manifestPlaceholders = [qrCodeHostName: "qr.swisscovid.ch"]
@@ -112,6 +115,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
manifestPlaceholders = [qrCodeHostName: "qr.swisscovid.ch"]
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index e7b2d5bc9..8e350d905 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -3,7 +3,6 @@ package ch.admin.bag.dp3t.checkin.generateqrcode
import android.app.Application
import android.content.Context
import android.graphics.Bitmap
-import android.util.Base64
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
@@ -27,8 +26,6 @@ import java.io.IOException
import kotlin.math.max
-private const val MASTER_PUBLIC_KEY_BASE64 =
- "lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"
private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
private const val ONE_HOUR_IN_MILLIS = 60 * ONE_MINUTE_IN_MILLIS
private const val AUTOMATIC_CHECKOUT_DELAY_MS = 12 * ONE_HOUR_IN_MILLIS
@@ -90,7 +87,7 @@ class QRCodeViewModel(application: Application) : AndroidViewModel(application)
swissCovidLocationData.toByteArray(),
System.currentTimeMillis() / 1000,
(System.currentTimeMillis() + QR_CODE_VALIDITY_DURATION_MS) / 1000,
- Base64Util.fromBase64(MASTER_PUBLIC_KEY_BASE64)
+ Base64Util.fromBase64(BuildConfig.QR_MASTER_PUBLIC_KEY_BASE_64)
)
From 27abfa9a3f5801c7b01e8bdb3af6dfd9d2d0767c Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 31 May 2021 17:36:22 +0200
Subject: [PATCH 120/207] Remove addToBackStack option from showFragment
extension function
---
.../generateqrcode/GenerateQrCodeFragment.kt | 3 ++-
.../dp3t/checkin/generateqrcode/QrCodeFragment.kt | 15 ++++-----------
.../bag/dp3t/extensions/FragmentExtensions.kt | 5 +----
.../java/ch/admin/bag/dp3t/home/HomeFragment.java | 4 ++--
4 files changed, 9 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
index c65f7563d..87b65ff5e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/GenerateQrCodeFragment.kt
@@ -34,7 +34,8 @@ class GenerateQrCodeFragment : Fragment() {
private fun generateQrCode(title: String) {
qrCodeViewModel.generateAndSaveQrCode(title).observe(viewLifecycleOwner) {
- showFragment(QrCodeFragment.newInstance(it), addToBackStack = false)
+ requireActivity().supportFragmentManager.popBackStack()
+ showFragment(QrCodeFragment.newInstance(it))
}
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
index f76f7d62d..1e8b9808e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeFragment.kt
@@ -20,11 +20,8 @@ import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel
import ch.admin.bag.dp3t.checkin.checkinflow.CheckInFragment
import ch.admin.bag.dp3t.checkin.models.CheckInState
import ch.admin.bag.dp3t.checkin.models.QRCodePayload
-import ch.admin.bag.dp3t.extensions.getSubtitle
-import ch.admin.bag.dp3t.extensions.toQrCodePayload
-import ch.admin.bag.dp3t.extensions.toVenueInfo
import ch.admin.bag.dp3t.databinding.FragmentQrCodeBinding
-import ch.admin.bag.dp3t.extensions.combineWith
+import ch.admin.bag.dp3t.extensions.*
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import com.google.protobuf.ByteString
import org.crowdnotifier.android.sdk.model.VenueInfo
@@ -45,9 +42,9 @@ class QrCodeFragment : Fragment() {
private val tracingViewModel: TracingViewModel by activityViewModels()
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FragmentQrCodeBinding.inflate(layoutInflater).apply {
- cancelButton.setOnClickListener { parentFragmentManager.popBackStack() }
+ cancelButton.setOnClickListener { requireActivity().supportFragmentManager.popBackStack() }
val venueInfo = QRCodePayload.parseFrom(arguments?.get(KEY_VENUE_INFO) as ByteString).toVenueInfo()
titleTextview.text = venueInfo.title
subtitleTextview.setText(venueInfo.getSubtitle())
@@ -85,11 +82,7 @@ class QrCodeFragment : Fragment() {
private fun showCheckInFragment(venueInfo: VenueInfo) {
crowdNotifierViewModel.checkInState =
CheckInState(false, venueInfo, System.currentTimeMillis(), System.currentTimeMillis(), 0)
- requireActivity().supportFragmentManager.beginTransaction()
- .setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit)
- .replace(R.id.main_fragment_container, CheckInFragment.newInstance(isSelfCheckin = true))
- .addToBackStack(CheckInFragment::class.java.canonicalName)
- .commit()
+ showFragment(CheckInFragment.newInstance(isSelfCheckin = true))
}
private fun printPdf(file: File) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
index b0aacf6e0..432a426f3 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/FragmentExtensions.kt
@@ -7,7 +7,6 @@ import ch.admin.bag.dp3t.R
fun Fragment.showFragment(
fragment: Fragment,
@IdRes container: Int = R.id.main_fragment_container,
- addToBackStack: Boolean = true,
modalAnimation: Boolean = false
) {
requireActivity().supportFragmentManager.beginTransaction()
@@ -19,10 +18,8 @@ fun Fragment.showFragment(
} else {
this.setCustomAnimations(R.anim.slide_enter, R.anim.slide_exit, R.anim.slide_pop_enter, R.anim.slide_pop_exit)
}
- if (addToBackStack) {
- this.addToBackStack(fragment::class.java.canonicalName)
- }
}
+ .addToBackStack(fragment::class.java.canonicalName)
.replace(container, fragment)
.commit()
}
\ No newline at end of file
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 3edae7802..2c7e7b2f1 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
@@ -260,9 +260,9 @@ private void showReportsFragment() {
boolean isReportedPositive = tracingViewModel.getTracingStatusInterface().isReportedAsInfected();
if (((checkinReports > 0 && tracingReports > 0) || checkinReports > 1) && !isReportedPositive) {
FragmentExtensionsKt
- .showFragment(this, ReportsOverviewFragment.newInstance(), R.id.main_fragment_container, true, false);
+ .showFragment(this, ReportsOverviewFragment.newInstance(), R.id.main_fragment_container, false);
} else {
- FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), R.id.main_fragment_container, true, false);
+ FragmentExtensionsKt.showFragment(this, ReportsFragment.newInstance(null), R.id.main_fragment_container, false);
}
}
From 7b82c6a5cbce1571bc588220b4bf59be4c84530c Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 31 May 2021 17:41:19 +0200
Subject: [PATCH 121/207] Add Abnahme Master Key
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index f13efd81f..68039f576 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -86,7 +86,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt-a.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr-a.swisscovid.ch"'
- buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"RHk4TO95YVnihWuCSVnbQ3ow-AcO-BXZ9caWeO_GP7o-vEiowJXTkKSUAf77n4oAmzshDvFVLkQFsA72JfBcaC8QSIT9owk83tnDuwpUuDfFgiBq_KXqcA2UcxaInmQS"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.abnahme.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.abnahme.BUCKET_PUBLIC_KEY}\""
applicationIdSuffix '.abnahme'
From 19f56e1ee1d0fa149c80169cb85d22d4778d4cc9 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Mon, 31 May 2021 23:11:26 +0200
Subject: [PATCH 122/207] Remove test flavour Appcenter github action
---
.github/workflows/appcenter_test.yml | 26 --------------------------
1 file changed, 26 deletions(-)
delete mode 100644 .github/workflows/appcenter_test.yml
diff --git a/.github/workflows/appcenter_test.yml b/.github/workflows/appcenter_test.yml
deleted file mode 100644
index 1bf3f23f2..000000000
--- a/.github/workflows/appcenter_test.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: Android Build
-
-on:
- push:
- branches: [ master, develop ]
- pull_request:
- branches: [ master ]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
- - name: Build with Gradle
- uses: ./.github/actions/gradle_docker
- with:
- gradle-cmd: assembleTeschtRelease -PkeystorePassword=${{secrets.KEYSTORE_PASSWORD}} -PkeyAliasPassword=${{secrets.KEY_ALIAS_PASSWORD}}
- - name: upload artefact to App Center
- uses: wzieba/AppCenter-Github-Action@v1.0.0
- with:
- appName: ${{secrets.APPCENTER_ORGANIZATION}}/${{secrets.APPCENTER_APP_TEST}}
- token: ${{secrets.APPCENTER_API_TOKEN}}
- group: Internal
- file: app/build/outputs/apk/tescht/release/app-tescht-release.apk
From 3cff7db0452f40bce3b1470feaf3bec98d0daa46 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 09:42:07 +0200
Subject: [PATCH 123/207] Fix some Reminder Options Bugs
---
.../checkin/checkinflow/CheckInFragment.kt | 2 +-
.../bag/dp3t/extensions/VenueInfoExtensions.kt | 18 +++++++++++++-----
app/src/main/res/values/app_styles.xml | 2 ++
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
index 6ec7a2d1e..30d57a842 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckInFragment.kt
@@ -66,7 +66,7 @@ class CheckInFragment : Fragment() {
}
reminderToggleGroup.removeAllViews()
- val reminderOptions = venueInfo.getReminderDelayOptions().toMutableList()
+ val reminderOptions = venueInfo.getReminderDelayOptions(requireContext()).toMutableList()
reminderOptions.add(0, ReminderOption(0L))
for (option in reminderOptions) {
val toggleButton =
diff --git a/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueInfoExtensions.kt b/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueInfoExtensions.kt
index fbf2dc4ef..0d69000fe 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueInfoExtensions.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/extensions/VenueInfoExtensions.kt
@@ -1,5 +1,6 @@
package ch.admin.bag.dp3t.extensions
+import android.content.Context
import ch.admin.bag.dp3t.checkin.models.QRCodePayload
import ch.admin.bag.dp3t.checkin.models.ReminderOption
import com.google.protobuf.InvalidProtocolBufferException
@@ -19,12 +20,19 @@ fun VenueInfo.getAutoCheckoutDelay() = getSwissCovidLocationData().automaticChec
fun VenueInfo.getCheckoutWarningDelay() = getSwissCovidLocationData().checkoutWarningDelayMs
-fun VenueInfo.getReminderDelayOptions(): List {
- if (getSwissCovidLocationData().reminderDelayOptionsMsList.size == 0) {
+fun VenueInfo.getReminderDelayOptions(context: Context): List {
+ val filteredResult = getSwissCovidLocationData().reminderDelayOptionsMsList.asSequence()
+ .filter { it >= ONE_MINUTE_IN_MILLIS && it < getAutoCheckoutDelay() }
+ .sorted()
+ .map { ReminderOption(it) }
+ .distinctBy { it.getDisplayString(context) }
+ .take(4)
+ .toList()
+ return if (filteredResult.isEmpty()) {
//Fallback if reminderDelayOptionsMsList is empty (30, 60, 120 and 240 minutes)
- return listOf(30, 60, 120, 240).map { ReminderOption(it * ONE_MINUTE_IN_MILLIS) }
+ listOf(30, 60, 120, 240).map { ReminderOption(it * ONE_MINUTE_IN_MILLIS) }
+ } else {
+ filteredResult
}
- return getSwissCovidLocationData().reminderDelayOptionsMsList.asSequence().distinct().filter { it > 0 }.sorted()
- .map { ReminderOption(it) }.take(4).toList()
}
diff --git a/app/src/main/res/values/app_styles.xml b/app/src/main/res/values/app_styles.xml
index dea786d64..1ffb6f391 100644
--- a/app/src/main/res/values/app_styles.xml
+++ b/app/src/main/res/values/app_styles.xml
@@ -32,6 +32,8 @@
0dp1false
+ 0dp
+ 0dp@drawable/reminder_toggle_background_color@drawable/reminder_toggle_text_color@drawable/reminder_toggle_text_color
From 54f8b2ef54408552bf8ac5fe8e9820e9bda497fc Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 10:29:05 +0200
Subject: [PATCH 124/207] Implemented minimum exposure overlap of 15 min
---
app/build.gradle | 2 --
.../ch/admin/bag/dp3t/checkin/CrowdNotifierViewModel.java | 3 ++-
.../bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java | 6 ++----
.../bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java | 1 -
.../dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java | 4 +++-
common/build.gradle | 2 +-
instant/build.gradle | 2 --
7 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 68039f576..7b6241f38 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -178,8 +178,6 @@ dependencies {
prodImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
logImplementation "org.dpppt:dp3t-sdk-android:$dp3t_sdk_version"
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-13'
-
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.fragment:fragment-ktx:1.3.3'
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 eeccb4f1f..2fc8a1247 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
@@ -35,6 +35,7 @@
public class CrowdNotifierViewModel extends AndroidViewModel {
private static final long MAX_DURATION_WITHOUT_SUCCESSFUL_DOWNLOAD = 24 * 60 * 60 * 1000L;
+ private static final long MIN_EXPOSURE_OVERLAP = 15 * 1000L * 60;
private final MutableLiveData> exposures = new MutableLiveData<>();
private final MutableLiveData timeSinceCheckIn = new MutableLiveData<>(0L);
@@ -129,7 +130,7 @@ public void refreshTraceKeys() {
traceKeyLoadingState.setValue(LoadingState.FAILURE);
} else {
SecureStorage.getInstance(getApplication()).setLastSuccessfulCheckinDownload(System.currentTimeMillis());
- CrowdNotifier.checkForMatches(traceKeys, getApplication());
+ CrowdNotifier.checkForMatches(traceKeys, MIN_EXPOSURE_OVERLAP,getApplication());
refreshExposures();
traceKeyLoadingState.setValue(LoadingState.SUCCESS);
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
index 662673d47..39ca57805 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -1,11 +1,9 @@
package ch.admin.bag.dp3t.checkin.checkinflow;
import android.Manifest;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -187,11 +185,11 @@ public synchronized void onQRCodeFound(String qrCodeData) {
showCheckInFragment();
if (getActivity() != null) getActivity().runOnUiThread(() -> indicateInvalidQrCode(QRScannerState.VALID));
} catch (QrUtils.QRException e) {
- handleInvalidQRCodeExceptions(qrCodeData, e);
+ handleInvalidQRCodeExceptions(e);
}
}
- private void handleInvalidQRCodeExceptions(String qrCodeData, QrUtils.QRException e) {
+ private void handleInvalidQRCodeExceptions(QrUtils.QRException e) {
if (e instanceof QrUtils.InvalidQRCodeVersionException) {
if (getActivity() != null) getActivity().runOnUiThread(() -> {
isQRScanningEnabled = false;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
index 8fcb9a6e3..0f41767e5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/DiaryRecyclerViewAdapter.java
@@ -18,7 +18,6 @@
import ch.admin.bag.dp3t.checkin.diary.items.ItemVenueVisitDayHeader;
import ch.admin.bag.dp3t.checkin.diary.items.VenueVisitRecyclerItem;
import ch.admin.bag.dp3t.extensions.CommonVenueInfoExtensionsKt;
-import ch.admin.bag.dp3t.extensions.VenueInfoExtensionsKt;
import ch.admin.bag.dp3t.util.StringUtil;
public class DiaryRecyclerViewAdapter extends RecyclerView.Adapter {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
index 6cfb77a75..34767bfc1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
@@ -28,6 +28,7 @@ public class CrowdNotifierKeyLoadWorker extends Worker {
private static final String WORK_TAG = "ch.admin.bag.dp3t.checkin.networking.CrowdNotifierKeyLoadWorker";
private static final int DAYS_TO_KEEP_VENUE_VISITS = 14;
private static final int REPEAT_INTERVAL_MINUTES = 120;
+ private static final long MIN_EXPOSURE_OVERLAP = 15 * 1000L * 60;
private static final String LOG_TAG = "KeyLoadWorker";
public static void startKeyLoadWorker(Context context) {
@@ -59,7 +60,8 @@ public Result doWork() {
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(ACTION_NEW_TRACE_KEY_SYNC));
return Result.retry();
}
- List exposures = CrowdNotifier.checkForMatches(problematicEventInfos, getApplicationContext());
+ List exposures =
+ CrowdNotifier.checkForMatches(problematicEventInfos, MIN_EXPOSURE_OVERLAP, getApplicationContext());
if (!exposures.isEmpty()) {
showExposureNotification();
}
diff --git a/common/build.gradle b/common/build.gradle
index 740c7df98..269feaf6f 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -66,7 +66,7 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.google.protobuf:protobuf-lite:3.0.1'
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-10'
+ api 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-14'
api 'com.google.android.gms:play-services-instantapps:17.0.0'
}
\ No newline at end of file
diff --git a/instant/build.gradle b/instant/build.gradle
index cbb1b73e7..b7abc581d 100644
--- a/instant/build.gradle
+++ b/instant/build.gradle
@@ -68,6 +68,4 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
- implementation 'org.crowdnotifier:crowdnotifier-sdk-android:4.0-dev-10'
-
}
\ No newline at end of file
From 8e21d368c531ef4f6627ef784dfd05b847066927 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 10:46:25 +0200
Subject: [PATCH 125/207] Update strings
---
common/src/main/res/values-bs/strings.xml | 397 +++++++++++----------
common/src/main/res/values-de/strings.xml | 11 +-
common/src/main/res/values-es/strings.xml | 15 +-
common/src/main/res/values-fr/strings.xml | 15 +-
common/src/main/res/values-hr/strings.xml | 395 +++++++++++----------
common/src/main/res/values-it/strings.xml | 13 +-
common/src/main/res/values-pt/strings.xml | 382 ++++++++++-----------
common/src/main/res/values-rm/strings.xml | 13 +-
common/src/main/res/values-sq/strings.xml | 395 +++++++++++----------
common/src/main/res/values-sr/strings.xml | 395 +++++++++++----------
common/src/main/res/values-ti/strings.xml | 397 +++++++++++----------
common/src/main/res/values-tr/strings.xml | 401 +++++++++++-----------
common/src/main/res/values/strings.xml | 15 +-
13 files changed, 1361 insertions(+), 1483 deletions(-)
diff --git a/common/src/main/res/values-bs/strings.xml b/common/src/main/res/values-bs/strings.xml
index e615aac2b..6d709a2ab 100644
--- a/common/src/main/res/values-bs/strings.xml
+++ b/common/src/main/res/values-bs/strings.xml
@@ -194,7 +194,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sa Covid šifrom..."
- "Zaustavljanje lanca širenja infekcije"
+ "Upozorenje u slučaju moguće infekcije""Unos Covid šifre"
@@ -228,7 +228,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Praćenje je deaktivirano""Aktivirajte praćenje da bi aplikacija radila.""Bluetooth isključen"
- "Uključite Bluetooth na vašem uređaju da bi praćenje radilo."
+ "Uključite Bluetooth na vašem uređaju, kako bi funkcionisalo anonomno praćenje.""Uključi Bluetooth""Korišćenje Bluetooth-a odbijeno""Dozvolite korišćenje Bluetooth-a da bi praćenje radilo."
@@ -260,8 +260,8 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sledite uputstva za izolaciju koja ste dobili"
- "Praćenje završeno"
- "Dalji kontakti se više ne čuvaju."
+ "Deaktivirani kontakti i prijave "
+ "Dalji kontakti se više ne čuvaju""Obratite se""Sledite""Uputstva za samoizolaciju"
@@ -274,7 +274,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Testiran pozitivno"
- "Postoji mogućnost infekcije."
+ "Postoji mogućnost infekcije""Testirani ste pozitivno na virus korona."
@@ -331,7 +331,7 @@ ispred virusa"
"Pomoću aplikacije Swiss Covid svi možemo da pomognemo u suzbijanju širenja virusa korona."
- "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korona."
+ "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korone.""Privacy by Design"
@@ -346,7 +346,7 @@ ispred virusa"
"Sačuvani nasumični ID-ovi se čuvaju na vašem mobilnom telefonu i brišu se nakon 14 dana."
- "Kako funkcioniše aplikacija?"
+ "Kontakti""Prepoznavanje kontakta
@@ -422,7 +422,7 @@ Pridržavajte se dobijenih uputstava o izolaciji.""Aplikacija je spremna"
- "Hvala vam što pomažete u prekidanju lanca širenja infekcije."
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone.""Pokreni"
@@ -449,7 +449,7 @@ Pridržavajte se dobijenih uputstava o izolaciji."
"Moguća infekcija"
- "Postoji sumnja na moguću infekciju."
+ "Postoji sumnja da ste se inficirali.""Vremenska greška"
@@ -585,7 +585,7 @@ Time se potrošnja baterije samo neznatno povećava."
"Šta su poruke?"
- "Aplikacija redovno proverava da li su drugi korisnici sa kojima ste bili u kontaktu prijavili pozitivan test.
+ "Aplikacija redovno proverava da li su drugi korisnici, sa kojima ste bili u kontaktu, prijavili pozitivan test.
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
@@ -626,7 +626,7 @@ Time se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikac
"Šta se šalje?"
- "Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.
+ "Šalju se samo privatni ključevi iz vaše aplikacije i šifrovani ID podaci vaših prijava, a ne šalju se lični podaci.
Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije."
@@ -637,7 +637,7 @@ Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Važna napomena"
+ "Informacije za korišćenje ""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma."
@@ -711,8 +711,7 @@ Osim toga, mogući su sledeći simptomi:
"Aktiviraj praćenje"
-
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""Aktiviraj"
@@ -741,10 +740,10 @@ Osim toga, mogući su sledeći simptomi:
"Da li želite da završite fazu izolacije?"
-
+
"Da li ste sigurni da želite da obrišete poruku?""Ignoriši optimizaciju baterije"
- "Da biste uvek mogli da prepoznate kontakte, optimizacija baterije za ovu aplikaciju mora biti isključena."
+ "Kako bi aplikacija funkcionisala, optimizacija baterije mora biti isključena.""Dozvoli""OK""Samo za ovu aplikaciju"
@@ -767,8 +766,7 @@ Osim toga, mogući su sledeći simptomi:
"COVID-19: obavještenja deaktivirano"
-
- "Aktivirajte Obavijesti o izloženosti bolesti COVID-19 da bi praćenje radilo."
+ "Aktivirajte funkciju {TRACING_SETTING_TEXT}, kako bi funkcionisalo anonimno praćenje.""Slanje poruke nije uspelo. Dozvolite deljenje nasumičnog ID-a sa aplikacijom SwissCovid."
@@ -810,9 +808,8 @@ Ako trenutno na uređaju nije dostupno ažuriranje, probajte kasnije ponovo.""Aplikacija SwissCovid ostaje aktivna čak i kad se zatvori."
- "SwissCovid
-prekida lanac
-širenja infekcije."
+ "SwissCovid - brze informacije u slučaju rizika od zaraze.
+""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma.""Da li ostajem anoniman?"
@@ -823,7 +820,7 @@ prekida lanac
"Obavijesti o izloženosti bolesti COVID-19"
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""SwissCovid"
@@ -916,7 +913,7 @@ Schwarzenburgstrasse 157
"Putovanja"
- "SwissCovid funkcijoniše u sledećim državama:"
+ "SwissCovid-kontakti funkcionišu u sledećim državama:""Šta to znači?"
@@ -946,12 +943,6 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1233,9 +1224,9 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"Beenden (završi)"
- "Kontakti više neće da se čuvaju.
+ "Kontakti se više neće čuvati i nove prijave nisu više moguće.
-Praćenje može ponovo da se aktivira po završetku izolacije."
+Ove dve funkcije mogu se ponovo aktivirati po završetku izolacije.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1267,7 +1258,7 @@ Praćenje može ponovo da se aktivira po završetku izolacije.""https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?
+ "Da li ste bez sluha ili imate oštećenje sluha i ne možete nazvati?
Onda pošaljite e-mail na covid-support@medgate.ch"
@@ -1322,8 +1313,8 @@ Vreme je odlučujući faktor u suzbijanju Corona virusa: što je vreme između p
"Grafika pokazuje prijavljene nove infekcije poslednjih 28 dana. Time imate pregled o aktuelnom razvoju."
- "Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?
-U tom slučaju kontaktirajte Infoline Coronavirus:"
+ "Pozitivno ste testirani (PCR test ili brzi antigen test) i ni posle 4 sata niste dobili Covid kod?
+U tom slučaju kontaktirajte nas ovde:""Još niste dobili Covid šifru?"
@@ -1362,13 +1353,13 @@ U tom slučaju kontaktirajte Infoline Coronavirus:""Ko se obaveštava"
- "Obaveštavaju se svi kontakti u periodu vaše faze zaražavanja od {ONSET_DATE} do unosa kovid šifre, ukoliko je na osnovu distance i trajanja postojala mogućnost zaraze."
+ "Obaveštavaju se svi kontakti u periodu vaše faze infekcioznosti od {ONSET_DATE} do unosa Covid koda, ukoliko je na osnovu distance i trajanja ili zbog prijave na istoj lokaciji postojala mogućnost zaraze.""{ONSET_DATE} – danas"
- "Na taj način pomažete u zaustavljanju lanca zaraze."
+ "Na taj način pomažete da se upozore drugi o mogućosti zaraze.""Privatni ključevi vaše aplikacije poslati su za sledeći period:"
@@ -1407,363 +1398,359 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Do you want to check out?"
+ "Želite li da se odjavite?"
- "Automatic check-out"
+ "Automatska odjava"
- "You were checked out automatically."
+ "Automatski ste odjavljeni."
- "Scan QR code and check in"
+ "Skenirati QR kod i prijaviti se"
- "No valid QR code!"
+ "Nevažeći QR kod!"
- "From"
+ "Od"
- "To"
+ "Do"
- "Add note"
+ "Dodati zabelešku"
- "e.g. who were you with?"
+ "npr. S kim ste bili tamo?"
- "Done"
+ "Gotovo"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Off"
+ "Isključeno"
- "Authentication needed to access the diary"
+ "Identifikujte se, kako biste pristupili dnevniku"
- "{HOURS} h"
+ "časovi {HOURS} h"
- "{MINUTES}'"
+ "minute {MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "Aplikaciji je potreban pristup kameri, da bi se skenirao QR kod."
- "My check-ins"
+ "Moje prijave"
- "Check-in"
+ "Prijava"
- "The camera is needed to scan the QR code."
+ "Kamera je potrebna, kako bi se skenirao QR kod."
- "Allow Face ID to securely access your diary."
+ "Dozvolite identifikaciju lica, da biste mogli bezbedno pristupiti vašem dnevniku."
- "Authentication needed to access the diary"
+ "Identifikujte se za pristup dnevniku"
- "Check-out"
+ "Odjava "
- "Check-in"
+ "Prijava"
- "Check-in"
+ "Prijava"
- "Generate QR code"
+ "Napraviti QR kod"
- "Offer check-ins?"
+ "Ponuditi prijave?"
- "Check in now"
+ "Sada se prijaviti"
- "The reminder could not be set"
+ "Podsećanje nije uspelo da se unese"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Da biste dobijali podsetnike za odjavu, morate da dozvolite \ \"Obaveštenja \\" u podešavanjima aplikacije."
- "Customise"
+ "Prilagoditi"
- "Edit"
+ "Urediti"
- "No entries available yet"
+ "Još nema unosa"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate SwissCovid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid-u treba pristup kameri na vašem aparatu. Kamera se koristi, kako bi se skenirao QR kod."
- "Allow access to camera"
+ "Dozvoliti pristup kameri"
- "e.g. room or event name"
+ "npr. soba ili naziv događaja"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
- "Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Odštampati PDF"
+ "Informacija"
+ "Mrežna greška"
+ "Proverite vašu vezu sa internetom "
+ "Pokušajte ponovo"
+ "Deaktivirati obaveštenja"
+ "Promenite podešavanja, kako biste bili direktno bili obavešteni o prijavi."
+ "Promenite podešavanja"
+ "Nema pristupa kameri"
+ "Aplikaciji je potreban pristup kameri, kako bi se mogao skenirati QR kod."
+ "Greška"
+ "QR kod je još nevažeći"
+ "QR kod je nevažeći "
+ "Upravo ste prijavljeni."
+ "Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje."
+ "Potrebno ažuriranje"
+ "Sada ažurirati"
+ "Odjava "
+ "Snooze mirovanje"
+ "Poruka o trajnom prijavljivanju"
+ "Prijavljeni ste {TIME} časova."
- "Save"
+ "Osigurati"
- "Scan QR code"
+ "Skenirati QR kod"
- "Check-in deactivated"
+ "Deaktivirano prijavljivanje"
- "Check-in is not possible at the moment"
+ "Trenutno nije moguća prijava"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate Swiss-Covid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Prijavljeni ste"
+ "{DATE1} do {DATE2}"
+ "Više o Covid kodu"
- "Remove entry"
+ "Ukloniti unos"
- "Delete"
+ "Izbrisati"
- "Share"
+ "Podeliti"
- "Print"
+ "Štampati"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "Share check-in entries"
+ "Deliti unose o prijavama"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Kada delite unose prijave, aplikacija može istovremeno da obavesti lica, koja su se prijavila na istoj lokaciji."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Izabrati sve"
+ "Ne slati"
+ "Ne deliti"
+ "Zaista ne deliti?"
+ "Deljenje privatnog ključa pomaže «SvissCovid» da obavesti druge potencijalno zaražene ljude i spreči dalje širenje virusa. Ako ne delite privatne ključeve, potencijalno zaražene osobe neće biti obaveštene."
- "Remove entry"
+ "Ukloniti unos"
- "Your entry will be permanently removed from the list."
+ "Vaš unos će biti neopozivo uklonjen sa liste."
- "Completely remove"
+ "Ukoniti u potpunosti"
- "Only hide and keep receiving notifications?"
+ "Samo sakriti i nastaviti da budete obaveštavani?"
- "Tested positive?"
+ "Test pozitivan?"
- "Reactivate encounters and check-in"
+ "Kontakte i prijave ponovo aktivirati. "
- "{HOURS} h {MINUTES}'"
+ "časovi {HOURS} h minute {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Ne možete ponovo da se prijavite ako imate pozitivan test."
- "Generate a QR code that people can use to check in on site."
+ "Napravite QR kod, pomoću kojeg lica mogu da se prijave na lokaciji."
- "Are you sure you want to delete the QR code?"
+ "Želite li zaista da izbrišete QR kod? "
- "Self check-in"
+ "Samostalno prijavljivanje"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na lokaciji."
- "Saving not possible"
+ "Nije moguće obezbeđenje"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Odabrana vremena se preklapaju sa ostalim prijavama iz vašeg dnevnika. Molimo vas da ponovo proverite svoje podatke."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Vaši odabrani unosi za prijavu su poslati. Biće obaveštena sva lica koja su se istovremeno prijavili na istom mestu."
- "SwissCovid check-in"
+ "SwissCovid prijava
+
+"
- "Check in without leaving any data"
+ "Prijaviti se bez ostavljanja podataka"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "Pomoći aplikacije SwissCovid možete jednostavno da se prijavite kod nas ne ostavljajući svoje podatke. U slučaju rizične situacije, brzo ćete biti obavešteni u aplikaciji."
- "SwissCovid app"
+ "SwissCovid aplikacija"
- "Download now"
+ "Sada preuzeti"
- "Check in without leaving
-data"
+ "Prijaviti se bez ostavljanja podataka"
- "Enter your Covidcode here to alert others."
+ "Ovde unesite svoj Covid kod, kako biste upozorili druge."
- "Isolation ended?"
+ "Kraj izolacije?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Želite da ponudite prijave?"
+ "Čemu služi funkcija prijave?"
+ "Funkcija prijave bez unošenja podataka o kontaktima povećava funkcije aplikacije SwissCovid. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istom mestu."
+ "Nema centralnog čuvanja podataka"
+ "Za SwissCovid prijave ne treba unositi lične podatke kao što su ime, adresa ili telefonski broj i ne beleže se GPS podaci o lokaciji. Spisak prijava čuva se lokalno na vašem mobilnom telefonu. Lokalni podaci će se izbrisati nakon 14 dana."
+ "Napravite QR kod"
+ "Sa SwissCovid-QR kodom mogu se na licu mjesta prijaviti lica, a da ne ostavljaju svoje podatke. "
+ "To nije zamena za obavezni unos kontakt podataka"
+ "SwissCovid prijave nisu zamena za obavezu evidenciju kontakt podataka utvrđenu u „Pravilniku Covid 19 za posebne situacije“."
+ "Gde se mogu koristiti prijave?"
+ "Zašto služi funkcija prijave?"
+ "SwissCovid prijave se mogu koristiti tamo gde se ljudi sastaju, ali ne postoji obaveza beleženja podataka o kontaktima. Moguće lokacije:
+- Privatni događaji
+- Događaji u klubovima
+- Sastanci u profesionalnom okruženju kao što su sale za sastanke, kancelarije, menze itd. \""
+ "Funkcija prijave bez unošenja podataka o kontaktima proširuje funkcije aplikacije. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istoj lokaciji."
- "Risk situation"
+ "Rizična situacija"
- "Encounters"
+ "Kontakti"
- "Check-in"
+ "Prijava"
- "What should I do?"
+ "Šta treba da uradima?"
- "Positive check-in"
+ "Pozitivna prijava"
- "Positive check-ins/contacts"
+ "Pozitivne prijave / kontakti"
- "User QR code"
+ "QR kod korisnika"
- "Contact tracing QR code"
+ "QR kod za praćenje kontakata"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Ako je rezultat testa pozitivan, funkcija prijave se automatski deaktivira. Može se ponovo aktivirati nakon završetka izolacije."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Dodelite naziv, koji detaljnije opisuje lokaciju ili vaš događaj. Ovaj naziv će biti prikazan prilikom prijave."
+ "Šta možete da uradite?"
+ "Zaštititi kontakte"
+ "Obratiti pažnju na simptome"
+ "Vršiti testiranje"
+ "Mogli biste biti zarazni, a da to ne primetite. Pridržavajte se važećih higijenskih i zaštitnih mera i zaštitite svoju porodicu, prijatelje i okolinu."
+ "Proveravajte svoje zdravsveno stanje."
+ "Odmah se testirajte ako imate simptome. Čak i ako nema simptoma, test korone je važan i preporučuje se."
- "Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Prijava"
+ "Podaci nisu poslati"
+ "Nisu poslati privatni ključevi."
+ "Nazad"
+ "Ne slati nikakve podatke"
+ "Nisu poslati unosi za prijavu."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
- "Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Deliti izbor"
+ "Ne deliti"
+ "Prijaviti se bez unošenja podataka"
+ "Prijave"
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka."
+ "Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji."
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka. Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji. U ove svrhe ažurirani su uslovi korišćenja i izjava o zaštiti podataka."
+ "Preskočiti"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "Šifrovani unos čuva se u pozadini aplikacije 14 dana. U slučaju moguće infekcije, i dalje možete biti obavešteni."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Sada sakriti"
+ "Aplikacija vas može podsetiti da se odjavite u pravo vreme."
- "Or remove completely?"
+ "Ili potpuno ukloniti?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "Unos je potpuno uklonjen. Više ne možete biti upozoreni."
+ "Niko neće biti obavešten."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "Šta treba da znam o testiranju?"
+ "Instalirati aplikaciju i prijavite se"
+ "Završite konfiguraciju aplikacije, kako biste mogli da koristite funkciju praćenja."
+ "Završiti sada"
+ "Konfiguracija završena"
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone."
+ "Brze informacije u slučaju rizika od zaraze. ""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 42e94e259..09fb2a557 100644
--- a/common/src/main/res/values-de/strings.xml
+++ b/common/src/main/res/values-de/strings.xml
@@ -760,7 +760,7 @@ Zudem sind folgende Symptome möglich:
"Isolation beenden"
-
+
"Möchten Sie die Isolation wirklich beenden?""Akku-Optimierung ignorieren"
@@ -970,12 +970,6 @@ Damit auch Reisende aus dem Ausland gewarnt werden können, geschieht das unabh
"+41 58 387 77 80"
-
- "COVID-19-Kontaktmitteilungen"
-
-
- "COVID-19-Begegnungsmitteilungen"
-
"Begegnungsmitteilungen"
@@ -1795,9 +1789,6 @@ Dieser Titel wird beim Check-in angezeigt."
"Check-In"
-
-
- "Keine Daten gesendet"
diff --git a/common/src/main/res/values-es/strings.xml b/common/src/main/res/values-es/strings.xml
index 66d28d707..77773d607 100644
--- a/common/src/main/res/values-es/strings.xml
+++ b/common/src/main/res/values-es/strings.xml
@@ -173,7 +173,7 @@ De esta forma, está ayudando a parar las cadenas de contagio."
"Rastreo"
- "Síntomas de la enfermedad"
+ "tengo síntomas de la enfermedad""¿Qué hacer si…"
@@ -732,7 +732,7 @@ Pueden aparecer también los síntomas siguientes:
"Finalizar el aislamiento."
-
+
"¿De verdad quiere eliminar esta notificación?""Ignorar optimización de la batería""La aplicación solo funciona si la optimización de la batería está desconectada."
@@ -936,12 +936,6 @@ Al fin de poder alertar también a los viajeros del extranjero, esto ocurre inde
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1708,9 +1702,6 @@ Posibles utilizaciones:
"Check-in"
-
-
- "No se han enviado datos""No se han enviado claves privadas.""Volver"
@@ -1753,5 +1744,5 @@ Posibles utilizaciones:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 c3f7a79ef..057cc8715 100644
--- a/common/src/main/res/values-fr/strings.xml
+++ b/common/src/main/res/values-fr/strings.xml
@@ -740,8 +740,8 @@ Les symptômes suivants peuvent aussi apparaître :
"Terminer l'isolement"
-
- "Voulez-vous vraiment supprimer la notification ? "
+
+ "Voulez-vous vraiment supprimer la notification ?""Ignorer l'optimisation de la batterie""L’option d’optimisation de la batterie doit être désactivée pour que l’application fonctionne.""Autoriser"
@@ -945,12 +945,6 @@ d'ouvrir un site internet."
"+41 58 387 77 80"
-
-
-
-
-
-
"Notifications d’exposition""SwissCovid"
@@ -1739,9 +1733,6 @@ Appelez :"
"Connexion"
-
-
- "Pas de données transmises""Aucune clé privée n’a été transmise.""Retour"
@@ -1784,5 +1775,5 @@ Appelez :"
"https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 68c76ae3d..1fd361d7a 100644
--- a/common/src/main/res/values-hr/strings.xml
+++ b/common/src/main/res/values-hr/strings.xml
@@ -193,7 +193,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sa Covid šifrom..."
- "Zaustavljanje lanca širenja infekcije"
+ "Upozorenje u slučaju moguće infekcije""Unos Covid šifre"
@@ -227,7 +227,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Praćenje je deaktivirano""Aktivirajte praćenje da bi aplikacija radila.""Bluetooth isključen"
- "Uključite Bluetooth na vašem uređaju da bi praćenje radilo."
+ "Uključite Bluetooth na vašem uređaju, kako bi funkcionisalo anonomno praćenje.""Uključi Bluetooth""Korišćenje Bluetooth-a odbijeno""Dozvolite korišćenje Bluetooth-a da bi praćenje radilo."
@@ -259,8 +259,8 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sledite uputstva za izolaciju koja ste dobili"
- "Praćenje završeno"
- "Dalji kontakti se više ne čuvaju."
+ "Deaktivirani kontakti i prijave "
+ "Dalji kontakti se više ne čuvaju""Obratite se""Sledite""Uputstva za samoizolaciju"
@@ -273,7 +273,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Testiran pozitivno"
- "Postoji mogućnost infekcije."
+ "Postoji mogućnost infekcije""Testirani ste pozitivno na virus korona."
@@ -330,7 +330,7 @@ ispred virusa"
"Pomoću aplikacije Swiss Covid svi možemo da pomognemo u suzbijanju širenja virusa korona."
- "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korona."
+ "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korone.""Privacy by Design"
@@ -345,7 +345,7 @@ ispred virusa"
"Sačuvani nasumični ID-ovi se čuvaju na vašem mobilnom telefonu i brišu se nakon 14 dana."
- "Kako funkcioniše aplikacija?"
+ "Kontakti""Prepoznavanje kontakta
@@ -421,7 +421,7 @@ Pridržavajte se dobijenih uputstava o izolaciji.""Aplikacija je spremna"
- "Hvala vam što pomažete u prekidanju lanca širenja infekcije."
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone.""Pokreni"
@@ -448,7 +448,7 @@ Pridržavajte se dobijenih uputstava o izolaciji."
"Moguća infekcija"
- "Postoji sumnja na moguću infekciju."
+ "Postoji sumnja da ste se inficirali.""Vremenska greška"
@@ -584,7 +584,7 @@ Time se potrošnja baterije samo neznatno povećava."
"Šta su poruke?"
- "Aplikacija redovno proverava da li su drugi korisnici sa kojima ste bili u kontaktu prijavili pozitivan test.
+ "Aplikacija redovno proverava da li su drugi korisnici, sa kojima ste bili u kontaktu, prijavili pozitivan test.
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
@@ -625,7 +625,7 @@ Time se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikac
"Šta se šalje?"
- "Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.
+ "Šalju se samo privatni ključevi iz vaše aplikacije i šifrovani ID podaci vaših prijava, a ne šalju se lični podaci.
Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije."
@@ -636,7 +636,7 @@ Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Važna napomena"
+ "Informacije za korišćenje ""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma."
@@ -710,7 +710,7 @@ Osim toga, mogući su sledeći simptomi:
"Aktiviraj praćenje"
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""Aktiviraj"
@@ -739,10 +739,10 @@ Osim toga, mogući su sledeći simptomi:
"Da li želite da završite fazu izolacije?"
-
+
"Da li ste sigurni da želite da obrišete poruku?""Ignoriši optimizaciju baterije"
- "Da biste uvek mogli da prepoznate kontakte, optimizacija baterije za ovu aplikaciju mora biti isključena."
+ "Kako bi aplikacija funkcionisala, optimizacija baterije mora biti isključena.""Dozvoli""OK""Samo za ovu aplikaciju"
@@ -764,7 +764,7 @@ Osim toga, mogući su sledeći simptomi:
"COVID-19: obavještenja deaktivirano"
- "Aktivirajte Obavijesti o izloženosti bolesti COVID-19 da bi praćenje radilo."
+ "Aktivirajte funkciju {TRACING_SETTING_TEXT}, kako bi funkcionisalo anonimno praćenje.""Slanje poruke nije uspelo. Dozvolite deljenje nasumičnog ID-a sa aplikacijom SwissCovid."
@@ -806,9 +806,8 @@ Ako trenutno na uređaju nije dostupno ažuriranje, probajte kasnije ponovo.""Aplikacija SwissCovid ostaje aktivna čak i kad se zatvori."
- "SwissCovid
-prekida lanac
-širenja infekcije."
+ "SwissCovid - brze informacije u slučaju rizika od zaraze.
+""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma.""Da li ostajem anoniman?"
@@ -819,7 +818,7 @@ prekida lanac
"Obavijesti o izloženosti bolesti COVID-19"
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""SwissCovid"
@@ -912,7 +911,7 @@ Schwarzenburgstrasse 157
"Putovanja"
- "SwissCovid funkcijoniše u sledećim državama:"
+ "SwissCovid-kontakti funkcionišu u sledećim državama:""Šta to znači?"
@@ -942,12 +941,6 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1229,9 +1222,9 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"Beenden (završi)"
- "Kontakti više neće da se čuvaju.
+ "Kontakti se više neće čuvati i nove prijave nisu više moguće.
-Praćenje može ponovo da se aktivira po završetku izolacije."
+Ove dve funkcije mogu se ponovo aktivirati po završetku izolacije.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1263,7 +1256,7 @@ Praćenje može ponovo da se aktivira po završetku izolacije.""https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?
+ "Da li ste bez sluha ili imate oštećenje sluha i ne možete nazvati?
Onda pošaljite e-mail na covid-support@medgate.ch"
@@ -1318,8 +1311,8 @@ Vreme je odlučujući faktor u suzbijanju Corona virusa: što je vreme između p
"Grafika pokazuje prijavljene nove infekcije poslednjih 28 dana. Time imate pregled o aktuelnom razvoju."
- "Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?
-U tom slučaju kontaktirajte Infoline Coronavirus:"
+ "Pozitivno ste testirani (PCR test ili brzi antigen test) i ni posle 4 sata niste dobili Covid kod?
+U tom slučaju kontaktirajte nas ovde:""Još niste dobili Covid šifru?"
@@ -1358,13 +1351,13 @@ U tom slučaju kontaktirajte Infoline Coronavirus:""Ko se obaveštava"
- "Obaveštavaju se svi kontakti u periodu vaše faze zaražavanja od {ONSET_DATE} do unosa kovid šifre, ukoliko je na osnovu distance i trajanja postojala mogućnost zaraze."
+ "Obaveštavaju se svi kontakti u periodu vaše faze infekcioznosti od {ONSET_DATE} do unosa Covid koda, ukoliko je na osnovu distance i trajanja ili zbog prijave na istoj lokaciji postojala mogućnost zaraze.""{ONSET_DATE} – danas"
- "Na taj način pomažete u zaustavljanju lanca zaraze."
+ "Na taj način pomažete da se upozore drugi o mogućosti zaraze.""Privatni ključevi vaše aplikacije poslati su za sledeći period:"
@@ -1403,363 +1396,359 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Do you want to check out?"
+ "Želite li da se odjavite?"
- "Automatic check-out"
+ "Automatska odjava"
- "You were checked out automatically."
+ "Automatski ste odjavljeni."
- "Scan QR code and check in"
+ "Skenirati QR kod i prijaviti se"
- "No valid QR code!"
+ "Nevažeći QR kod!"
- "From"
+ "Od"
- "To"
+ "Do"
- "Add note"
+ "Dodati zabelešku"
- "e.g. who were you with?"
+ "npr. S kim ste bili tamo?"
- "Done"
+ "Gotovo"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Off"
+ "Isključeno"
- "Authentication needed to access the diary"
+ "Identifikujte se, kako biste pristupili dnevniku"
- "{HOURS} h"
+ "časovi {HOURS} h"
- "{MINUTES}'"
+ "minute {MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "Aplikaciji je potreban pristup kameri, da bi se skenirao QR kod."
- "My check-ins"
+ "Moje prijave"
- "Check-in"
+ "Prijava"
- "The camera is needed to scan the QR code."
+ "Kamera je potrebna, kako bi se skenirao QR kod."
- "Allow Face ID to securely access your diary."
+ "Dozvolite identifikaciju lica, da biste mogli bezbedno pristupiti vašem dnevniku."
- "Authentication needed to access the diary"
+ "Identifikujte se za pristup dnevniku"
- "Check-out"
+ "Odjava "
- "Check-in"
+ "Prijava"
- "Check-in"
+ "Prijava"
- "Generate QR code"
+ "Napraviti QR kod"
- "Offer check-ins?"
+ "Ponuditi prijave?"
- "Check in now"
+ "Sada se prijaviti"
- "The reminder could not be set"
+ "Podsećanje nije uspelo da se unese"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Da biste dobijali podsetnike za odjavu, morate da dozvolite \ \"Obaveštenja \\" u podešavanjima aplikacije."
- "Customise"
+ "Prilagoditi"
- "Edit"
+ "Urediti"
- "No entries available yet"
+ "Još nema unosa"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate SwissCovid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid-u treba pristup kameri na vašem aparatu. Kamera se koristi, kako bi se skenirao QR kod."
- "Allow access to camera"
+ "Dozvoliti pristup kameri"
- "e.g. room or event name"
+ "npr. soba ili naziv događaja"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
- "Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Odštampati PDF"
+ "Informacija"
+ "Mrežna greška"
+ "Proverite vašu vezu sa internetom "
+ "Pokušajte ponovo"
+ "Deaktivirati obaveštenja"
+ "Promenite podešavanja, kako biste bili direktno bili obavešteni o prijavi."
+ "Promenite podešavanja"
+ "Nema pristupa kameri"
+ "Aplikaciji je potreban pristup kameri, kako bi se mogao skenirati QR kod."
+ "Greška"
+ "QR kod je još nevažeći"
+ "QR kod je nevažeći "
+ "Upravo ste prijavljeni."
+ "Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje."
+ "Potrebno ažuriranje"
+ "Sada ažurirati"
+ "Odjava "
+ "Snooze mirovanje"
+ "Poruka o trajnom prijavljivanju"
+ "Prijavljeni ste {TIME} časova."
- "Save"
+ "Osigurati"
- "Scan QR code"
+ "Skenirati QR kod"
- "Check-in deactivated"
+ "Deaktivirano prijavljivanje"
- "Check-in is not possible at the moment"
+ "Trenutno nije moguća prijava"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate Swiss-Covid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Prijavljeni ste"
+ "{DATE1} do {DATE2}"
+ "Više o Covid kodu"
- "Remove entry"
+ "Ukloniti unos"
- "Delete"
+ "Izbrisati"
- "Share"
+ "Podeliti"
- "Print"
+ "Štampati"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "Share check-in entries"
+ "Deliti unose o prijavama"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Kada delite unose prijave, aplikacija može istovremeno da obavesti lica, koja su se prijavila na istoj lokaciji."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Izabrati sve"
+ "Ne slati"
+ "Ne deliti"
+ "Zaista ne deliti?"
+ "Deljenje privatnog ključa pomaže «SvissCovid» da obavesti druge potencijalno zaražene ljude i spreči dalje širenje virusa. Ako ne delite privatne ključeve, potencijalno zaražene osobe neće biti obaveštene."
- "Remove entry"
+ "Ukloniti unos"
- "Your entry will be permanently removed from the list."
+ "Vaš unos će biti neopozivo uklonjen sa liste."
- "Completely remove"
+ "Ukoniti u potpunosti"
- "Only hide and keep receiving notifications?"
+ "Samo sakriti i nastaviti da budete obaveštavani?"
- "Tested positive?"
+ "Test pozitivan?"
- "Reactivate encounters and check-in"
+ "Kontakte i prijave ponovo aktivirati. "
- "{HOURS} h {MINUTES}'"
+ "časovi {HOURS} h minute {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Ne možete ponovo da se prijavite ako imate pozitivan test."
- "Generate a QR code that people can use to check in on site."
+ "Napravite QR kod, pomoću kojeg lica mogu da se prijave na lokaciji."
- "Are you sure you want to delete the QR code?"
+ "Želite li zaista da izbrišete QR kod? "
- "Self check-in"
+ "Samostalno prijavljivanje"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na lokaciji."
- "Saving not possible"
+ "Nije moguće obezbeđenje"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Odabrana vremena se preklapaju sa ostalim prijavama iz vašeg dnevnika. Molimo vas da ponovo proverite svoje podatke."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Vaši odabrani unosi za prijavu su poslati. Biće obaveštena sva lica koja su se istovremeno prijavili na istom mestu."
- "SwissCovid check-in"
+ "SwissCovid prijava
+
+"
- "Check in without leaving any data"
+ "Prijaviti se bez ostavljanja podataka"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "Pomoći aplikacije SwissCovid možete jednostavno da se prijavite kod nas ne ostavljajući svoje podatke. U slučaju rizične situacije, brzo ćete biti obavešteni u aplikaciji."
- "SwissCovid app"
+ "SwissCovid aplikacija"
- "Download now"
+ "Sada preuzeti"
- "Check in without leaving
-data"
+ "Prijaviti se bez ostavljanja podataka"
- "Enter your Covidcode here to alert others."
+ "Ovde unesite svoj Covid kod, kako biste upozorili druge."
- "Isolation ended?"
+ "Kraj izolacije?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Želite da ponudite prijave?"
+ "Čemu služi funkcija prijave?"
+ "Funkcija prijave bez unošenja podataka o kontaktima povećava funkcije aplikacije SwissCovid. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istom mestu."
+ "Nema centralnog čuvanja podataka"
+ "Za SwissCovid prijave ne treba unositi lične podatke kao što su ime, adresa ili telefonski broj i ne beleže se GPS podaci o lokaciji. Spisak prijava čuva se lokalno na vašem mobilnom telefonu. Lokalni podaci će se izbrisati nakon 14 dana."
+ "Napravite QR kod"
+ "Sa SwissCovid-QR kodom mogu se na licu mjesta prijaviti lica, a da ne ostavljaju svoje podatke. "
+ "To nije zamena za obavezni unos kontakt podataka"
+ "SwissCovid prijave nisu zamena za obavezu evidenciju kontakt podataka utvrđenu u „Pravilniku Covid 19 za posebne situacije“."
+ "Gde se mogu koristiti prijave?"
+ "Zašto služi funkcija prijave?"
+ "SwissCovid prijave se mogu koristiti tamo gde se ljudi sastaju, ali ne postoji obaveza beleženja podataka o kontaktima. Moguće lokacije:
+- Privatni događaji
+- Događaji u klubovima
+- Sastanci u profesionalnom okruženju kao što su sale za sastanke, kancelarije, menze itd. \""
+ "Funkcija prijave bez unošenja podataka o kontaktima proširuje funkcije aplikacije. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istoj lokaciji."
- "Risk situation"
+ "Rizična situacija"
- "Encounters"
+ "Kontakti"
- "Check-in"
+ "Prijava"
- "What should I do?"
+ "Šta treba da uradima?"
- "Positive check-in"
+ "Pozitivna prijava"
- "Positive check-ins/contacts"
+ "Pozitivne prijave / kontakti"
- "User QR code"
+ "QR kod korisnika"
- "Contact tracing QR code"
+ "QR kod za praćenje kontakata"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Ako je rezultat testa pozitivan, funkcija prijave se automatski deaktivira. Može se ponovo aktivirati nakon završetka izolacije."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Dodelite naziv, koji detaljnije opisuje lokaciju ili vaš događaj. Ovaj naziv će biti prikazan prilikom prijave."
+ "Šta možete da uradite?"
+ "Zaštititi kontakte"
+ "Obratiti pažnju na simptome"
+ "Vršiti testiranje"
+ "Mogli biste biti zarazni, a da to ne primetite. Pridržavajte se važećih higijenskih i zaštitnih mera i zaštitite svoju porodicu, prijatelje i okolinu."
+ "Proveravajte svoje zdravsveno stanje."
+ "Odmah se testirajte ako imate simptome. Čak i ako nema simptoma, test korone je važan i preporučuje se."
- "Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Prijava"
+ "Podaci nisu poslati"
+ "Nisu poslati privatni ključevi."
+ "Nazad"
+ "Ne slati nikakve podatke"
+ "Nisu poslati unosi za prijavu."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
- "Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Deliti izbor"
+ "Ne deliti"
+ "Prijaviti se bez unošenja podataka"
+ "Prijave"
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka."
+ "Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji."
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka. Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji. U ove svrhe ažurirani su uslovi korišćenja i izjava o zaštiti podataka."
+ "Preskočiti"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "Šifrovani unos čuva se u pozadini aplikacije 14 dana. U slučaju moguće infekcije, i dalje možete biti obavešteni."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Sada sakriti"
+ "Aplikacija vas može podsetiti da se odjavite u pravo vreme."
- "Or remove completely?"
+ "Ili potpuno ukloniti?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "Unos je potpuno uklonjen. Više ne možete biti upozoreni."
+ "Niko neće biti obavešten."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "Šta treba da znam o testiranju?"
+ "Instalirati aplikaciju i prijavite se"
+ "Završite konfiguraciju aplikacije, kako biste mogli da koristite funkciju praćenja."
+ "Završiti sada"
+ "Konfiguracija završena"
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone."
+ "Brze informacije u slučaju rizika od zaraze. ""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 82a4ebdd0..5e6fc52bc 100644
--- a/common/src/main/res/values-it/strings.xml
+++ b/common/src/main/res/values-it/strings.xml
@@ -745,7 +745,7 @@ viene disattivato automaticamente. Il tracciamento può essere riattivato al ter
"Isolamento concluso"
-
+
"Eliminare la segnalazione?""Ignora l'ottimizzazione della batteria""Per utilizzare l'app è necessario disattivare l'ottimizzazione della batteria."
@@ -950,12 +950,6 @@ Affinché anche i viaggiatori all'estero possano essere avvisati, la condivision
"+41 58 387 77 80"
-
-
-
-
-
-
"Notifiche di esposizione""SwissCovid"
@@ -1751,9 +1745,6 @@ Possibili luoghi d'impiego:
"Check-in"
-
-
- "Nessun dato inviato""Nessuna chiave privata inviata""Indietro"
@@ -1796,5 +1787,5 @@ Possibili luoghi d'impiego:
"https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 e2a649b86..800bfe8d2 100644
--- a/common/src/main/res/values-pt/strings.xml
+++ b/common/src/main/res/values-pt/strings.xml
@@ -104,7 +104,7 @@ Obrigado por ajudar a travar as cadeias de transmissão."
"Possível contágio"
- "Existe a possibilidade de um contágio.
+ "Existe a possibilidade de contágio.
"
@@ -229,7 +229,7 @@ Obrigado por ajudar a travar as cadeias de transmissão.""Rastreamento desativado""Ative o rastreamento para que a app funcione.""Bluetooth desligado"
- "Ligue o Bluetooth no seu dispositivo para que o rastreamento funcione."
+ "Ligue o Bluetooth no seu dispositivo para que o rastreamento anónimo funcione.""Ligar o Bluetooth""Utilização de Bluetooth recusada""Permita a utilização do Bluetooth para que o rastreamento funcione."
@@ -262,10 +262,10 @@ Obrigado por ajudar a travar as cadeias de transmissão."
"Siga as instruções que recebeu relativamente ao isolamento""Rastreamento terminado"
- "Não serão guardados mais contactos."
+ "Não serão memorizados contactos""Contacte o/a""Siga o/a"
- "As instruções relativas ao auto-isolamento"
+ "Instruções relativas ao auto-isolamento""Nova notificação"
@@ -275,7 +275,7 @@ Obrigado por ajudar a travar as cadeias de transmissão."
"Teste positivo"
- "Existe a possibilidade de um contágio."
+ "Existe a possibilidade de contágio""Testou positivo para o coronavírus."
@@ -332,7 +332,7 @@ linha informativa SwissCovid
"Com a app Swiss Covid, todos podem ajudar a conter a propagação do coronavírus."
- "A app informa-o, caso possa ter estado exposto ao coronavírus."
+ "O aplicativo informa-o, caso possa ter estado exposto ao coronavírus.""Privacy by Design"
@@ -347,7 +347,7 @@ linha informativa SwissCovid
"Os IDs aleatórios são guardados no seu telemóvel durante 14 dias, apagando-se automaticamente após esse tempo."
- "Como funciona a app?"
+ "Como funciona o aplicativo?""Deteta contactos através do Bluetooth
@@ -367,7 +367,7 @@ linha informativa SwissCovid
"
- "A app informa-o, caso possa ter estado exposto ao coronavírus."
+ "O aplicativo informa-o, caso possa ter estado exposto ao coronavírus.""Se agir da forma correta, estará a travar as cadeias de transmissão e a proteger os outros."
@@ -400,7 +400,7 @@ linha informativa SwissCovid
"Permitir as notificações"
- "Para receber informações da app, tem de permitir as notificações."
+ "Para receber informações do aplicativo, tem de permitir as notificações.""Permitir as notificações"
@@ -423,7 +423,7 @@ Siga as instruções que recebeu relativamente ao isolamento."
"A app está pronta"
- "Muito obrigado por ajudar a interromper as cadeias de transmissão."
+ "Muito obrigado por ajudar a conter as cadeias de transmissão.""Iniciar"
@@ -586,7 +586,7 @@ Isto aumenta muito pouco o consumo da bateria."
"O que são as notificações?"
- "A app verifica periodicamente se outros utilizadores com quem teve contacto comunicaram um teste positivo.
+ "O aplicativo verifica periodicamente se outros utilizadores com os quais teve contacto comunicaram um teste positivo.
Em caso de possível contágio, receberá uma notificação no ecrã de bloqueio."
@@ -627,9 +627,9 @@ Isso assegura que só casos confirmados sejam comunicados através do aplicativo
"Que dados são enviados?"
- "Só são enviadas as chaves privadas do seu app e nunca dados pessoais.
+ "Só são enviadas as chaves privadas do seu aplicativo e nunca dados pessoais.
-Desta forma, outros utilizadores do app SwissCovid ficam a saber que existe a possibilidade de um contágio."
+Desta forma, outros utilizadores do aplicativo SwissCovid ficam a saber que existe a possibilidade de contágio.""+41 58 387 77 78"
@@ -638,7 +638,7 @@ Desta forma, outros utilizadores do app SwissCovid ficam a saber que existe a po
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Aviso importante"
+ "Informações para a introdução""Embora não sejam enviados dados pessoais seus, é possível que alguém se recorde de um contacto consigo pela data."
@@ -672,7 +672,7 @@ Outros sintomas podem ser:
"Porque está o rastreamento desativado?"
- "Após um teste positivo, o rastreamento na app é automaticamente desativado. O rastreamento pode ser reativado logo que termine o isolamento."
+ "Após um teste positivo, o rastreamento no aplicativo é automaticamente desativado. O rastreamento pode ser reativado logo que termine o isolamento.""pt"
@@ -712,8 +712,7 @@ Outros sintomas podem ser:
"Ativar o rastreamento"
-
- "Para poder usar a app, tem de ativar a função Notificações de exposição à COVID-19."
+ "Para poder usar o aplicativo, tem de ativar a função de rastreamento para que seja possível reconhecer os contactos.""Ativar"
@@ -742,10 +741,10 @@ Outros sintomas podem ser:
"Terminar o isolamento"
-
+
"Pretende apagar a notificação?""Ignorar otimização da bateria"
- "A função de otimização da bateria tem de ser desligada para esta app para que ela possa detetar contactos em qualquer altura."
+ "A função de otimização da bateria tem de ser desligada para que este aplicativo funcione.""Permitir""OK""Só para esta app"
@@ -768,8 +767,7 @@ Outros sintomas podem ser:
"Notificações de exposição à COVID-19 desativada"
-
- "Ative a função Notificações de exposição à COVID-19 para que o rastreamento funcione."
+ "Ative {TRACING_SETTING_TEXT} para que o rastreamento anónimo funcione.""A mensagem não foi enviada. Por favor, autorize a partilha dos seus dados de IDs aleatórios com a app SwissCovid."
@@ -811,9 +809,8 @@ Se não houver de momento nenhuma atualização disponível no seu dispositivo,
"A SwissCovid mantém-se ativa, mesmo quando a app é desligada."
- "A SwissCovid
-trava as
-cadeias de transmissão."
+ "SwissCovid -
+informação rápida em caso de risco de contágio.""Embora não sejam enviados dados pessoais seus, é possível que alguém se recorde de um contacto consigo pela data.""Permaneço anónimo?"
@@ -824,7 +821,7 @@ cadeias de transmissão.""Notificações de exposição à COVID-19"
- "Para poder usar a app, tem de ativar a função Notificações de exposição à COVID-19."
+ "Para que seja possível reconhecer contactos, tem de ativar a função de rastreamento.""SwissCovid"
@@ -917,7 +914,7 @@ Schwarzenburgstrasse 157
"Viajar"
- "A SwissCovid funciona nos seguintes países:"
+ "O SwissCovid-Contactos funciona nos seguintes países:""O que significa isto?"
@@ -947,12 +944,6 @@ Isto acontece independentemente de ter estado no estrangeiro ou não para que os
"+41 58 387 77 80"
-
-
-
-
-
-
"Notificações de exposição""SwissCovid"
@@ -1234,9 +1225,9 @@ Isto acontece independentemente de ter estado no estrangeiro ou não para que os
"Finalizar"
- "Não serão guardados mais contactos.
+ "Não são memorizados mais contactos novos e check-ins novos também não são mais possíveis.
-Após o isolamento, o rastreamento pode ser reativado."
+Após o isolamento, ambas as funções podem ser reativadas.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1268,7 +1259,7 @@ Após o isolamento, o rastreamento pode ser reativado."
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Sofre de surdez ou de insuficiência auditiva e não pode ligar para a linha informativa do coronavírus?
+ "Sofre de surdez ou de insuficiência auditiva e não pode telefonar?
Nesse caso, envie um e-mail para covid-support@medgate.ch"
@@ -1324,7 +1315,7 @@ No combate ao coronavírus, o tempo é um fator decisivo: quanto menos tempo pas
"Foi testado positivo (teste PCR ou teste rápido de antigénio) e ainda não recebeu um código Covid depois de 4 hs?
-Contacte a linha de informação coronavírus:"
+Ligue para:""Ainda não recebeu um código COVID?"
@@ -1348,7 +1339,7 @@ Contacte a linha de informação coronavírus:"
"Países compatíveis:"
- "Para alertar também os viajantes de outros países, as chaves privadas são partilhadas com os apps de coronavírus dos países compatíveis."
+ "Para alertar também os viajantes de outros países, as chaves privadas são partilhadas com os aplicativos de coronavírus dos países compatíveis.""Total"
@@ -1363,13 +1354,13 @@ Contacte a linha de informação coronavírus:"
"Quem vai ser informado?"
- "No período da sua fase contagiosa de {ONSET_DATE} até à introdução do código Covid, todos os contactos são informados, se houver a possibilidade de infecção devido à distância e à duração."
+ "No período da sua fase contagiosa de {ONSET_DATE} até à introdução do código Covid, todos os contactos são informados, se houver a possibilidade de infeção devido à distância e à duração ou se você tiver feito check-in no mesmo lugar.""{ONSET_DATE} – hoje"
- "Com isso, você ajuda a parar as cadeias de infecção."
+ "Com isso, você pode ajudar a alertar outros sobre um eventual contágio.""Os códigos privados do seu aplicativo foram enviados para o seguinte período:"
@@ -1408,46 +1399,46 @@ Contacte a linha de informação coronavírus:"
"Em 1 dia"
- "Check-out reminder"
+ "Lembrete de check-out"
- "Do you want to check out?"
+ "Quer fazer check-out?"
- "Automatic check-out"
+ "Checkout automático"
- "You were checked out automatically."
+ "O seu check-out foi feito automaticamente."
- "Scan QR code and check in"
+ "Ler o código QR e fazer check-in"
- "No valid QR code!"
+ "O código QR não é válido!"
- "From"
+ "De"
- "To"
+ "a"
- "Add note"
+ "Acrescentar uma nota"
- "e.g. who were you with?"
+ "p. ex. Com quem você esteve lá?"
- "Done"
+ "Pronto"
- "Check-out reminder"
+ "Lembrete de checkout"
- "Off"
+ "Desligado"
- "Authentication needed to access the diary"
+ "Autentique-se para ter acesso ao diário""{HOURS} h"
@@ -1456,22 +1447,22 @@ Contacte a linha de informação coronavírus:"
"{MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "O aplicativo tem de ter acesso à câmara para poder ler o código QR."
- "My check-ins"
+ "Meus check-ins""Check-in"
- "The camera is needed to scan the QR code."
+ "A câmara é necessária para ler o código QR."
- "Allow Face ID to securely access your diary."
+ "Permita Face-ID para aceder seguramente ao seu diário."
- "Authentication needed to access the diary"
+ "Autentique-se para o acesso ao diário""Check-out"
@@ -1483,288 +1474,285 @@ Contacte a linha de informação coronavírus:"
"Check-in"
- "Generate QR code"
+ "Criar um código QR"
- "Offer check-ins?"
+ "Oferecer check-ins?"
- "Check in now"
+ "Fazer check-in agora"
- "The reminder could not be set"
+ "Não foi possível enviar o lembrete"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Para receber lembretes de check-out, é necessário permitir as \\"notificações\\" na configuração do aplicativo."
- "Customise"
+ "Adaptar"
- "Edit"
+ "Editar"
- "No entries available yet"
+ "Ainda não há registos"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Se ler um código QR SwissCovid para fazer check-in no local, um registo vai ser criado automaticamente aqui."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "O SwissCovid precisa ter acesso à câmara do seu dispositivo. A câmara só é usada para ler códigos QR."
- "Allow access to camera"
+ "Permitir o acesso à câmara"
- "e.g. room or event name"
+ "p. ex. sala ou nome do evento"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
- "Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Imprimir PDF"
+ "Informação"
+ "Erro de rede"
+ "Verifique a sua ligação à Internet."
+ "Tentar novamente"
+ "Notificações desativadas"
+ "Mude a configuração para ser informado diretamente em caso duma mensagem."
+ "Mudar a configuração"
+ "Nenhum acesso à câmara"
+ "O aplicativo tem de ter acesso à câmara para poder ler o código QR."
+ "Erro"
+ "O código QR ainda não é válido!"
+ "O código QR não é mais válido!"
+ "Você já fez check-in."
+ "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"
+ "Check-out"
+ "Repetir o alarme"
+ "Mensagem de check-in permanente"
+ "Você fez check-in às {TIME} horas."
- "Save"
+ "Salvar"
- "Scan QR code"
+ "Ler o código QR"
- "Check-in deactivated"
+ "Check-in desativado"
- "Check-in is not possible at the moment"
+ "Atualmente, não é possível fazer check-in"
- "Scan a SwissCovid QR code to check in on site."
+ "Leia o código QR SwissCovid para fazer check-in no local."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Se ler um código QR SwissCovid para fazer check-in no local, um registo vai ser criado automaticamente aqui."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Você fez check-in"
+ "{DATE1} a {DATE2}"
+ "Mais informações sobre o código Covid"
- "Remove entry"
+ "Remover o registo"
- "Delete"
+ "Apagar"
- "Share"
+ "Compartilhar"
- "Print"
+ "Imprimir"
- "Scan a SwissCovid QR code to check in on site."
+ "Leia um código QR SwissCovid para fazer check-in no local."
- "Share check-in entries"
+ "Compartilhar registos de check-in"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Se compartilhar os seus registos de check-in, o aplicativo pode informar pessoas que fizeram check-in no mesmo lugar e na mesma altura."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Selecionar todos"
+ "Não enviar"
+ "Não compartilhar"
+ "Realmente não deseja compartilhar?"
+ "O compartilhamento das suas chaves privada ajuda o «SwissCovid» a informar outras pessoas potencialmente infetadas e a evitar a propagação do coronavírus. Se não compartilhar as suas chaves privadas, pessoas potencialmente infetadas não serão informadas."
- "Remove entry"
+ "Remover o registo"
- "Your entry will be permanently removed from the list."
+ "O seu registo vai ser removido irrevogavelmente da lista."
- "Completely remove"
+ "Remover completamente"
- "Only hide and keep receiving notifications?"
+ "Só ocultar e continuar a receber notificações?"
- "Tested positive?"
+ "Testou positivo?"
- "Reactivate encounters and check-in"
+ "Voltar a ativar os contactos e check-ins.""{HOURS} h {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Você não pode mais fazer check-in depois de ser testado positivo."
- "Generate a QR code that people can use to check in on site."
+ "Gere um código QR com o qual pessoas podem fazer check-in no local."
- "Are you sure you want to delete the QR code?"
+ "Realmente deseja apagar o código QR?"
- "Self check-in"
+ "Check-in próprio"
- "Scan a SwissCovid QR code to check in on site."
+ "Leia um código QR SwissCovid para fazer check-in no local."
- "Saving not possible"
+ "Não é possível salvar"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Os seus períodos de tempo selecionados sobrepõe-se a outros check-ins no seu diário. Por favor, verifique novamente a sua introdução."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Os seus registos de check-in selecionados foram enviados. Todas as pessoas que fizeram check-in na mesma altura e no mesmo lugar vão ser notificadas."
- "SwissCovid check-in"
+ "SwissCovid-Check-in"
- "Check in without leaving any data"
+ "Fazer check-in sem deixar dados"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "Com o aplicativo SwissCovid você pode fazer check-in aqui sem deixar dados. Em caso de situação de risco, você vai ser informado rapidamente no aplicativo."
- "SwissCovid app"
+ "Aplicativo SwissCovid"
- "Download now"
+ "Baixar agora"
- "Check in without leaving
-data"
+ "Fazer check-in sem deixar dados"
- "Enter your Covidcode here to alert others."
+ "Introduza o seu código Covid aqui para alertar outras pessoas.""Isolation ended?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Terminar o isolamento?"
+ "Porque uma função de check-in?"
+ "A função de check-in sem recolha de dados de contacto amplia o aplicativo SwissCovid. Com esta função você pode não só alertar contactos próximos, mas também pessoas que fizeram check-in no mesmo lugar e na mesma altura, em caso de teste positivo."
+ "Nenhum armazenamento de dados centralizado"
+ "Para os Swiss Covid check-ins não é necessário indicar dados pessoais como nome, endereço ou número de telefone e dados de localização GPS também não são recolhidos. A lista dos check-ins é memorizada localmente no seu smartphone. Depois de 14 dias os dados locais são apagados."
+ "Gere um código QR"
+ "Com um código QR SwissCovid-QR pessoas podem fazer check-in no local sem deixar dados."
+ "Não substitui a recolha de dados de contacto obrigatória"
+ "SwissCovid-Check-ins não substituem a obrigação da recolha de dados de contacto especificada no «Regulamento Covid-19 situação especial»."
+ "Onde podem ser utilizados check-ins?"
+ "Porque uma função de check-in?"
+ "SwissCovid-Check-ins podem ser usados, onde pessoas se encontram, mas não existe uma obrigação de recolher os dados de contacto.
+
+Lugares de utilização possíveis:
+– Eventos privados
+– Eventos de associações
+– Reuniões profissionais como, p. ex. na sala de reunião, escritórios, cantinas, etc."
+ "A função de check-in sem recolha de dados de contacto amplia o aplicativo SwissCovid. Com esta função você pode não só alertar contactos próximos, mas também pessoas que fizeram check-in no mesmo lugar e na mesma altura, em caso de teste positivo."
- "Risk situation"
+ "Situação de risco"
- "Encounters"
+ "Contactos""Check-in"
- "What should I do?"
+ "O que devo fazer?"
- "Positive check-in"
+ "Check-in positivo"
- "Positive check-ins/contacts"
+ "Check-ins positivos / Contactos"
- "User QR code"
+ "Código QR do utilizador"
- "Contact tracing QR code"
+ "Código QR de rastreamento de contactos"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Após um resultado de teste positivo, a função de check-in é desativada automaticamente. Ela só pode ser reativada após o seu isolamento."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Dê um título que descreve o lugar ou o seu evento.
+Este título vai ser indicado no check-in."
+ "O que pode fazer?"
+ "Proteger os contactos"
+ "Observar os sintomas"
+ "Deixar testar-se"
+ "Você já pode ser contagioso, sem percebê-lo. Observe as medidas de higiene e proteção vigentes e proteja a sua família, os amigos e o seu ambiente."
+ "Observe o seu estado de saúde."
+ "Deixe testar-se imediatamente se aparecerem sintomas. Mesmo se não aparecerem sintomas, um teste de corona é conveniente e recomendado.""Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Não foram enviados dados."
+ "Não foram enviadas chaves privadas."
+ "Voltar"
+ "Não enviar dados"
+ "Não foram enviados registos de check-in."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
+ "Compartilhar a seleção"
+ "Não compartilhar nada"
+ "Fazer check-in sem deixar dados""Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Com SwissCovid-Check-ins você pode fazer check-in num local ou numa reunião com o aplicativo."
+ "A sua presença só é memorizada no aplicativo. Você não tem de deixar dados pessoais no aplicativo."
+ "Com SwissCovid-Check-ins você pode fazer check-in num local ou numa reunião com o aplicativo. A sua presença só é memorizada no aplicativo. Você não tem de deixar dados pessoais no aplicativo. Para tal, as condições de utilização e a política de privacidade foram atualizadas."
+ "Saltar"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "O registo vai ser memorizado codificado no fundo do aplicativo por 14 dias. Em caso de contágio possível você vai poder ser notificado, mesmo assim."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Só ocultar"
+ "O aplicativo pode lembrar de fazer check-out na altura certa."
- "Or remove completely?"
+ "Ou remover completamente?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "O registo vai ser removido completamente. Você não vai mais poder ser alertado."
+ "Ninguém vai ser notificado."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "O que devo saber sobre os testes?"
+ "Instalar o aplicativo e fazer check-in"
+ "Conclua a configuração do aplicativo para poder usar a função de rastreamento."
+ "Concluir agora"
+ "Configuração concluída"
+ "Agradecemos a sua ajuda para conter a propagação do coronavírus."
+ "Informação rápida em caso de risco de infeção""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 4f92d7d88..3de7f0790 100644
--- a/common/src/main/res/values-rm/strings.xml
+++ b/common/src/main/res/values-rm/strings.xml
@@ -737,7 +737,7 @@ Pussaivels èn er ils suandants sintoms:
"Terminar l'isolaziun"
-
+
"Vulais Vus propi stizzar l’avis?""Ignorar l'optimaziun da l'accu""Per pudair identifitgar ils inscunters d’in cuntin, sto l'optimaziun da l'accu esser deactivada per questa app."
@@ -942,12 +942,6 @@ Per che er las viagiaturas ed ils viagiaturs da l'exteriur possian vegnir infurm
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1716,9 +1710,6 @@ Possible locations:
"Check-in"
-
-
- "No data sent""No private key was sent.""Back"
@@ -1761,5 +1752,5 @@ Possible locations:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 3ecbc1323..8912c8f0d 100644
--- a/common/src/main/res/values-sq/strings.xml
+++ b/common/src/main/res/values-sq/strings.xml
@@ -104,7 +104,7 @@ Në këtë mënyrë na ndihmoni, për të ndaluar zinxhirin e infeksionit.""Infeksion i mundshëm"
- "Ekziston dyshimi i një infeksioni të mundshëm."
+ "Ju mund të jeni infektuar""APP është aktiv"
@@ -194,7 +194,7 @@ Në këtë mënyrë na ndihmoni, për të ndaluar zinxhirin e infeksionit.""Me kodin Covid..."
- "Ndalo zinxhirin e infeksionit"
+ "Informoni për infektimet e mundshme""Fut kodin e Covid"
@@ -228,7 +228,7 @@ Në këtë mënyrë na ndihmoni, për të ndaluar zinxhirin e infeksionit.""Gjurmimi i çaktivizuar"
"Aktivizoni gjurmimin që aplikacioni të funksionojë.""Bluetooth fikur"
- "Ndizni Bluetooth në pajisje në mënyrë që gjurmimi të funksionojë."
+ "Ndizni Bluetooth në pajisjen tuaj në mënyrë që të funksionojë gjurmimi anonim.""Ndiz Bluetooth""Përdorimi i Bluetooth u refuzua""Lejoni përdorimin e Bluetooth, në mënyrë që gjurmimi të funksionojë."
@@ -260,11 +260,11 @@ Në këtë mënyrë na ndihmoni, për të ndaluar zinxhirin e infeksionit."
"Ndiqni udhëzimet e caktuara për izolimin"
- "Gjurmimi i përfunduar"
- "Nuk ruhet më asnjë kontakt."
+ "Kontaktet dhe Check-in janë çaktivizuar"
+ "Nuk ruhet asnjë kontakt""Kontaktoni""Ndiqni"
- "Udhëzimet për vetë-izolimin"
+ "Udhëzimet për izolimin""Njoftim i ri"
@@ -274,7 +274,7 @@ Në këtë mënyrë na ndihmoni, për të ndaluar zinxhirin e infeksionit.""Pozitiv në test"
- "Ekziston dyshimi i një infeksioni të mundshëm."
+ "Ju mund të jeni infektuar""Ju keni rezultuar pozitiv me koronavirusin."
@@ -331,7 +331,7 @@ virusit"
"Me aplikacionin Swiss Covid të gjithë ne mund të ndihmojmë për të frenuar përhapjen e koronavirusin."
- "Aplikacioni ju informon nëse jeni ekspozuar potencialisht me koronavirusin."
+ "Aplikacioni ju informon nëse jeni ekspozuar potencialisht me koronavirus.""Privacy by Design"
@@ -346,7 +346,7 @@ virusit"
"ID e ruajtura të rastit mbeten në telefonin tuaj celular dhe fshihen pas 14 ditësh."
- "Si funksionon aplikacioni?"
+ "Kontaktet""Identifikon kontaktet
@@ -366,7 +366,7 @@ falë Bluetooth"
infeksioni të mundshëm"
- "Aplikacioni ju informon nëse jeni ekspozuar potencialisht me koronavirusin."
+ "Aplikacioni ju informon nëse jeni ekspozuar potencialisht me koronavirus.""Me sjelljen e duhur ju mund të ndërprisni zinxhirin e infeksionit dhe të mbroni të tjerët."
@@ -422,7 +422,7 @@ Respektoni udhëzimet e caktuara për izolimin."
"Aplikacioni është instaluar"
- "Shumë faleminderit që na ndihmoni për të ndërprerë zinxhirin e infeksionit."
+ "Shumë faleminderit që na ndihmoni për të ndaluar zgjerimin e koronavirusit.""Nis"
@@ -449,7 +449,7 @@ Respektoni udhëzimet e caktuara për izolimin."
"Infeksion i mundshëm"
- "Ekziston dyshimi i një infeksioni të mundshëm."
+ "Ju mund të jeni infektuar""Gabim i orës"
@@ -585,9 +585,9 @@ Nga kjo, konsumi i baterisë rritet vetëm pak."
"Çfarë janë mesazhet?"
- "Aplikacioni kontrollon rregullisht nëse përdoruesit e tjerë që keni takuar kanë rezultuar pozitivë në test.
+ "Aplikacioni kontrollon rregullisht nëse përdoruesit e tjerë që keni takuar ose ata që janë kontrolluar në të njëjtin vend kanë rezultuar pozitivë në test.
-Në rast infeksioni të mundshëm informoheni me një mesazh në ekranin e mbyllur."
+Në rast infektimi të mundshëm informoheni me një mesazh në ekranin e mbyllur.""Infoline SwissCovid ju këshillon për mundësinë e një testi falas."
@@ -626,9 +626,9 @@ Kjo siguron se vetëm rastet e konfirmuara raportohen përmes aplikacionit.""Çfarë dërgohet?"
- "Dërgohen vetëm kodet e identifikimit të aplikacionit tuaj, asnjë e dhënë personale.
+ "Dërgohen vetëm çelësat personal të aplikacionit tuaj dhe ID e koduara të Check-ins tuaj, asnjë e dhënë personale.
-Në këtë mënyrë mësoni përdoruesit e tjerë të alikacionit SwissCovid, të cilët mundësisht janë infektuar."
+Në këtë mënyrë mësoni përdorueset dhe përdoruesit e tjerë të aplikacionit SwissCovid, të cilët ka mundësi të jenë infektuar.""+41 58 387 77 78"
@@ -637,10 +637,10 @@ Në këtë mënyrë mësoni përdoruesit e tjerë të alikacionit SwissCovid, t
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Udhëzim i rëndësishëm"
+ "Informacion për futjen e të dhënave"
- "Edhe pse nuk dërgohet asnjë e dhënë personale, mund të ndodhë që dikush mund të kujtohet për kontaktin me anë të datës."
+ "Edhe pse nuk dërgohet asnjë e dhënë personale, mund të ndodhë që dikush mund të kujtohet për kontaktin me ju me anë të datës.""Pranoj"
@@ -711,8 +711,7 @@ Simptoma gastrointestinale (të përziera, të vjella, diarre, dhimbje abdominal
"Aktivizo gjurmimin"
-
- "Për të përdorur aplikacionin, funksioni Njoftime rreth COVID-19 duhet të jetë i aktivizuar."
+ "Funksioni i gjurmimit duhet të jetë i aktivizuar, në mënyrë që të dallohen kontaktet.""Aktivizo"
@@ -741,10 +740,10 @@ Simptoma gastrointestinale (të përziera, të vjella, diarre, dhimbje abdominal
"Fundi i izolimit"
-
+
"Dëshironi vërtet ta fshini njoftimin?""Injoro optimizimin e baterisë"
- "Për të identifikuar gjithnjë kontaktet, optimizimi i baterisë duhet të jetë i fikur për këtë aplikacion."
+ "Në mënyrë që aplikacioni të funksionojë, optimizimi i baterisë duhet të jetë i fikur.""Lejo""OK""Vetëm për këtë aplikacion"
@@ -767,8 +766,7 @@ Simptoma gastrointestinale (të përziera, të vjella, diarre, dhimbje abdominal
"Njoftime rreth ekspozimit ndaj COVID-19 i çaktivizuar"
-
- "Aktivizoni Njoftime rreth ekspozimit ndaj COVID-19, në mënyrë që gjurmimi të funksionojë."
+ "Aktivizoni {TRACING_SETTING_TEXT}, në mënyrë që të funksionojë gjurmimi anonim.""Mesazhi nuk u dërgua dot. Ju lutemi lejoni ndarjen e ID të rastit tuaj me aplikacionin SwissCovid."
@@ -811,10 +809,9 @@ Në rast se në pajisjen tuaj nuk disponohet ndërkohë asnjë Update, ju lutemi
"SwissCovid mbetet aktiv, edhe nëse aplikacioni mbyllet."
- "SwissCovid
-ndalon zinxhirin
-e infeksionit."
- "Edhe pse nuk dërgohet asnjë e dhënë personale, mund të ndodhë që dikush mund të kujtohet për kontaktin me anë të datës."
+ "SwissCovid –
+në rastin e një rreziku infektimi ju informon shpejt."
+ "Po, por edhe pse nuk dërgohet asnjë e dhënë personale, mund të ndodhë që dikush mund të kujtohet për kontaktin me ju me anë të datës.""A mbetem unë anonim?"
@@ -824,7 +821,7 @@ e infeksionit."
"Njoftime rreth ekspozimit ndaj COVID-19"
- "Për të përdorur aplikacionin, funksioni Njoftime rreth COVID-19 duhet të jetë i aktivizuar."
+ "Funksioni i gjurmimit duhet të jetë i aktivizuar, në mënyrë që të dallohen kontaktet.""SwissCovid"
@@ -917,7 +914,7 @@ Schwarzenburgstrasse 157
"Udhëtimi"
- "SwissCovid punon në shtetet më poshtë:"
+ "Kontaktet e SwissCovid funksionojnë në shtetet më poshtë:""Cfarë do të thotë kjo?"
@@ -947,12 +944,6 @@ Në mënyrë që udhëtarët nga jashtë shtetit të mund të paralajmërohen, k
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1234,9 +1225,9 @@ Në mënyrë që udhëtarët nga jashtë shtetit të mund të paralajmërohen, k
"Përfundo"
- "Nuk ruhen më takime.
+ "Nuk ruhen më kontakte të reja dhe gjithashtu Check-ins e reja nuk janë më të mundshme.
-Gjurmimi mund të riaktivizohet pas izolimit."
+Të dyja funksionet mund të aktivizohen përsëri, pasi të ketë mbaruar izolimi.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1268,7 +1259,7 @@ Gjurmimi mund të riaktivizohet pas izolimit."
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "A jeni i shurdhër ose keni probleme dëgjimi dhe nuk mund të telefononi linjën telefonike për koronavirusin Infoline Coronavirus?
+ "A jeni i shurdhër ose keni probleme dëgjimi dhe nuk mund të telefononi?
Atëherë na dërgoni një email te covid-support@medgate.ch"
@@ -1323,8 +1314,8 @@ Koha është faktor vendimtar në luftën ndaj koronavirusit: sa më e shkurtër
"Grafiku tregon infeksionet e reja të raportuara gjatë 28 ditëve të fundit. Kjo jep një pasqyrë të rasteve aktuale."
- "A keni testuar pozitiv (nga test PCR ose test i shpejtë antigjeni) dhe nuk keni marrë një kod Covid pas 4 orësh?
-Atëherë kontaktoni qendrën telefonike të koronavirusit:"
+ "A keni dalë pozitiv në test (testi PCR ose testi i shpejtë antigjen) dhe nuk keni marrë një kod Covid pas 4 orësh?
+Atëherë na telefononi këtu:""Nuk keni marrë ende asnjë kod Covid?"
@@ -1348,7 +1339,7 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:""Shtetet e përfshira:"
- "Në mënyrë që udhëtarët nga jashtë shtetit të mund të paralajmërohen, kodet e identifikimit shpërndahen me aplikacionet për koronavirusin të vendeve të përfshira."
+ "Në mënyrë që udhëtarët nga jashtë shtetit të mund të paralajmërohen pas një kontakti, çelësat personal shpërndahen me aplikacionet për koronavirusin të vendeve të përfshira.""Total"
@@ -1363,13 +1354,13 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"Kush njoftohet?"
- "Të gjithë kontaktet në periudhën e fazës tuaj të infektimit nga {ONSET_DATE} deri në hyrjen e kodit Covid njoftohen nëse ekziston mundësia e infektimit për shkak të distancës dhe kohëzgjatjes."
+ "Të gjitha kontaktet në periudhën e fazës suaj të infektimit nga {ONSET_DATE} deri në futjen e kodit Covid njoftohen, nëse ekziston rreziku i një infektimi – për shkak të distancës dhe kohëzgjatjes ose nëse jeni kontrolluar në të njëjtin vend.""{ONSET_DATE} – sot"
- "Duke vepruar kështu, ju ndihmoni të ndaloni zinxhirin e infektimit."
+ "Duke vepruar kështu, ju ndihmoni të informoni të tjerët për një infektim të mundshëm.""Çelësat privatë të aplikacionit tuaj janë dërguar për periudhën kohore vijuese:"
@@ -1408,46 +1399,46 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"Në 1 ditë"
- "Check-out reminder"
+ "Kujtesa e Check-out"
- "Do you want to check out?"
+ "Dëshironi të kontrolloheni?"
- "Automatic check-out"
+ "Checkout automatik"
- "You were checked out automatically."
+ "Ju u kontrolluat automatikisht."
- "Scan QR code and check in"
+ "Skanoni dhe kontrolloni kodin QR"
- "No valid QR code!"
+ "Kodi QR i pavlefshëm!"
- "From"
+ "Nga"
- "To"
+ "Deri"
- "Add note"
+ "Shtoni shënimin"
- "e.g. who were you with?"
+ "psh. Me kë ishit aty?"
- "Done"
+ "Gati"
- "Check-out reminder"
+ "Kujtesa e Check-out"
- "Off"
+ "Fikur"
- "Authentication needed to access the diary"
+ "Autentifikohuni për aksesin në ditar""{HOURS} h"
@@ -1456,315 +1447,313 @@ Atëherë kontaktoni qendrën telefonike të koronavirusit:"
"{MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "Aplikacionit i nevojitet aksesi në kamera për të skanuar kodin QR."
- "My check-ins"
+ "Check-ins e mia""Check-in"
- "The camera is needed to scan the QR code."
+ "Kamera nevojitet për të skanuar kodin QR."
- "Allow Face ID to securely access your diary."
+ "Lejoni Face-ID për të pasur akses të sigurt në ditarin tuaj."
- "Authentication needed to access the diary"
+ "Autentifikohuni për aksesin në ditar"
- "Check-out"
+ "Check out""Check-in"
- "Check-in"
+ "Check-In"
- "Generate QR code"
+ "Krijoni kodin QR"
- "Offer check-ins?"
+ "Ofroni Check-ins?"
- "Check in now"
+ "Kontrollohuni tani"
- "The reminder could not be set"
+ "Kujtesa nuk u vendos dot"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Në mënyrë që të merrni kujtesa nga Check-out, duhet të lejoni \\"Njoftimet\'’ te cilësimet e aplikacionit."
- "Customise"
+ "Përshtat"
- "Edit"
+ "Përpuno"
- "No entries available yet"
+ "Nuk ekziston ende asnjë shënim"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Nëse skanoni një kod QR të SwissCovid për t’u kontrolluar në vend, atëherë këtu krijohet automatikisht një shënim."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid i nevojitet aksesi në kamerën e pajisjes suaj. Kamera përdoret vetëm për të skanuar kodin QR."
- "Allow access to camera"
+ "Lejoni aksesin e kamerës"
- "e.g. room or event name"
+ "p.sh. Hapësira ose emri për rastin"
- "Print PDF"
+ "Printoni në PDF""Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
+ "Gabim i rrjetit"
+ "Kontrolloni lidhjen tuaj të internetit."
+ "Provoheni përsëri"
+ "Njoftimet janë çaktivizuar"
+ "Ndryshoni cilësimet për t’u informuar direkt me një njoftim."
+ "Ndryshoni cilësimet"
+ "Asnjë akses në kamera"
+ "Aplikacionit i nevojitet aksesi në kamera për të skanuar kodin QR."
+ "Gabim"
+ "Kodi QR nuk është ende i vlefshëm"
+ "Kodi QR nuk është më i vlefshëm"
+ "Ju jeni kontrolluar tashmë."
+ "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""Check out""Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Mesazh i përhershëm i Checkin"
+ "Ju jeni kontrolluar nga ora {TIME}."
- "Save"
+ "Siguro"
- "Scan QR code"
+ "Skanoni kodin QR"
- "Check-in deactivated"
+ "Check-In i çaktivizuar"
- "Check-in is not possible at the moment"
+ "Ndërkohë nuk është i mundur asnjë Check-in"
- "Scan a SwissCovid QR code to check in on site."
+ "Skanoni një kod QR të SwissCovid për t’u kontrolluar në vend."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Nëse skanoni një kod QR të SwissCovid për t’u kontrolluar në vend, atëherë këtu krijohet automatikisht një shënim."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Ju jeni kontrolluar"
+ "{DATE1} deri {DATE2}"
+ "Më shumë për kodin Covid"
- "Remove entry"
+ "Hiqni shënimin"
- "Delete"
+ "Fshi"
- "Share"
+ "Shpërndaj"
- "Print"
+ "Printo"
- "Scan a SwissCovid QR code to check in on site."
+ "Skanoni një kod QR të SwissCovid për t’u kontrolluar në vend."
- "Share check-in entries"
+ "Shpërndani shënimet e Check-in"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Nëse shpërndani shënimet tuaja të Check-in, atëherë aplikacioni njofton personat, të cilët janë kontrolluar në të njëjtën kohë në të njëjtin vend."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Zgjidhni të gjitha"
+ "Mos dërgoni"
+ "Mos shpërndani"
+ "Vërtet të mos shpërndani?"
+ "Shpërndarja e çelësave tuaj personal ndihmon ''SwissCovid'' për të njoftuar persona të tjerë të infektuar potencialisht dhe për të parandaluar zgjerimin e mëtejshëm të virusit. Nëse nuk i shpërndani çelësat tuaj personal, atëherë nuk njoftohet asnjë person i infektuar potencialisht."
- "Remove entry"
+ "Hiqni shënimin"
- "Your entry will be permanently removed from the list."
+ "Shënimi juaj hiqet nga lista në mënyrë të paprapësueshme."
- "Completely remove"
+ "Hiqni plotësisht"
- "Only hide and keep receiving notifications?"
+ "Thjesht ta fshini dhe të njoftoheni gjithsesi?"
- "Tested positive?"
+ "Keni dalë pozitiv në test?"
- "Reactivate encounters and check-in"
+ "Aktivizoni përsëri kontaktet dhe Check-in.""{HOURS} h {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Ju nuk mund të kontrolloheni më, sepse keni dalë pozitiv në test."
- "Generate a QR code that people can use to check in on site."
+ "Krijoni një kod QR, me të cilin të kontrolloni personat në vend."
- "Are you sure you want to delete the QR code?"
+ "Dëshironi vërtet ta fshini kodin QR?"
- "Self check-in"
+ "Self Check-In"
- "Scan a SwissCovid QR code to check in on site."
+ "Skanoni një kod QR të SwissCovid për t’u kontrolluar në vend."
- "Saving not possible"
+ "Sigurimi nuk është i mundur"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Kohët tuaja të zgjedhura përkojnë me Check-ins e tjera nga ditari juaj. Kontrolloni edhe njëherë të dhënat tuaja."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Shënimet tuaja të zgjedhura të Check-in u dërguan. Të gjithë personat, të cilët janë kontrolluar në të njëjtën kohë në të njëjtin vend, do të njoftohen."
- "SwissCovid check-in"
+ "Check-in i SwissCovid"
- "Check in without leaving any data"
+ "Kontrolli pa lënë të dhëna"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "Me aplikacionin SwissCovid mund të kontrolloheni thjesht te ne, pa lënë të dhënat tuaja. Në rastin e një situate rreziku do të informoheni shpejt në aplikacion."
- "SwissCovid app"
+ "Aplikacioni \"SwissCovid\""
- "Download now"
+ "Shkarkoni tani"
- "Check in without leaving
-data"
+ "Kontrolli pa lënë të dhëna
+"
- "Enter your Covidcode here to alert others."
+ "Futni këtu kodin tuaj Covid për të informuar të tjerët."
- "Isolation ended?"
+ "Keni përfunduar izolimin?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Dëshironi të ofroni Check-ins?"
+ "Përse duhet një funksion i Check-in?"
+ "Aplikacioni SwissCovid zgjeron funksionin e Check-in pa futjen e të dhënave të kontaktit. Me këtë funksion, në rastin kur keni dalë pozitiv në test, ju jo vetëm që mund të informoni kontaktet e afërta, por edhe personat që janë kontrolluar në të njëjtën kohë në të njëjtin vend."
+ "Asnjë ruajtje qendrore e të dhënave"
+ "Për Check-ins e SwissCovid nuk duhet futur asnjë e dhënë personale si emri, adresa ose numri i telefonit dhe nuk përfshihen as të dhënat e vendndodhjes në GPS. Lista e Check-ins ruhet lokalisht në smartfonin tuaj. Pas 14 ditësh të dhënat lokale fshihen përsëri."
+ "Krijoni një kod QR"
+ "Me një kod QR të SwissCovid mund të kontrollohen në vend personat pa lënë të dhënat e tyre."
+ "Asnjë zëvendësim për përfshirjen e detyruar të të dhënave të kontaktit."
+ "Check-ins e SwissCovid nuk janë zëvendësim i detyrimit për të regjistruar të dhënat e kontaktit të përcaktuara në \"Rregulloren e Covid-19, Situatë e veçantë\"."
+ "Ku mund të përdoren Check-ins?"
+ "Përse duhet një funksion i Check-in?"
+ "Check-ins e SwissCovid mund të përdoren atje, ku mblidhen njerëzit, por që nuk ka asnjë detyrim për regjistrimin e të dhënave të kontaktit.
+
+Vendet e mundshme të përdorimit:
+– Arsye personale
+– Arsye shoqërore
+– Takime në një ambient profesional siç janë sallat e mbledhjeve, zyrat, mensat, etj."
+ "Aplikacioni SwissCovid zgjeron funksionin e Check-in pa futjen e të dhënave të kontaktit. Me këtë funksion, në rastin kur keni dalë pozitiv në test, ju jo vetëm që mund të informoni kontaktet e afërta, por edhe personat që janë kontrolluar në të njëjtën kohë në të njëjtin vend."
- "Risk situation"
+ "Situata e rrezikut"
- "Encounters"
+ "Kontaktet""Check-in"
- "What should I do?"
+ "Çfarë nevojitet të bëj?"
- "Positive check-in"
+ "Check-in që ka dalë pozitiv"
- "Positive check-ins/contacts"
+ "Check-ins që kanë dalë pozitiv / kontaktet"
- "User QR code"
+ "Kodi ynë QR"
- "Contact tracing QR code"
+ "Kodi QR i Contact Tracing"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Pas një rezultati të testit që ka dalë pozitiv funksioni i Check-in është çaktivizuar. Ai mund të aktivizohet përsëri pas përfundimit të izolimit tuaj."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Jepni një titull që e përshkruan më afër vendin ose arsyen tuaj.
+Ky titull shfaqet gjatë Check-in."
+ "Çfarë mund të bëni?"
+ "Mbroni kontaktet"
+ "Kini parasysh simptomat"
+ "Kryeni testin"
+ "Ju tashmë mund të jeni i infektuar pa e vënë re. Mbani parasysh masat e higjienës dhe të mbrojtjes dhe mbroni familjen tuaj, miqtë tuaj dhe mjedisin tuaj rrethues."
+ "Mbikëqyrni gjendjen tuaj shëndetësore."
+ "Testohuni menjëherë gjatë shfaqjes së simptomave. Edhe nëse nuk shfaqet asnjë simptomë, testi i koronës është i arsyeshëm dhe i rekomandueshëm."
- "Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Check-In"
+ "Nuk u dërgua asnjë e dhënë"
+ "Nuk u dërgua asnjë çelës personal."
+ "Kthehu"
+ "Mos dërgoni asnjë të dhënë"
+ "Nuk u dërgua asnjë shënim për Check-in."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
+ "Shpërndani zgjedhjen"
+ "Mos shpërndani asgjë"
+ "Kontrolli pa lënë të dhëna""Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Me Check-ins e SwissCovid mund të kontrolloheni në një vend ose në një takim me aplikacion."
+ "Prania juaj ruhet vetëm në aplikacion. Ju nuk duhet të lini asnjë të dhënë personale në aplikacion."
+ "Me Check-ins e SwissCovid mund të kontrolloheni në një vend ose në një takim me aplikacion. Prania juaj ruhet vetëm në aplikacion. Ju nuk duhet të lini asnjë të dhënë personale në aplikacion. Për më tepër u përditësuan kushtet e përdorimit dhe politika për privatësinë."
+ "Tejkalo"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "Shënimi ruhet i koduar 14 ditë në sfondin e aplikacionit. Në rastin e një infektimit të mundshëm, përsëri mund të njoftoheni."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Thjesht fshini"
+ "Aplikacioni mund t’ju kujtojë për t’u kontrolluar përsëri në kohën e duhur."
- "Or remove completely?"
+ "Ose ta hiqni plotësisht?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "Shënimi hiqet plotësisht. Ju nuk mund të informoheni më."
+ "Nuk njoftohet askush."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "Çfarë duhet të di unë rreth testimeve?"
+ "Instaloni dhe kontrolloni aplikacionin."
+ "Mbylleni konfigurimin e aplikacionit për të përdorur funksionin Tracking."
+ "Mbylleni tani"
+ "Konfigurimi u mbyll"
+ "Shumë faleminderit që na ndihmoni për të ndaluar zgjerimin e koronavirusit."
+ "Në rastin e një rreziku të infektimit informoheni shpejt.""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 ad4d2cdca..a4162b5f0 100644
--- a/common/src/main/res/values-sr/strings.xml
+++ b/common/src/main/res/values-sr/strings.xml
@@ -193,7 +193,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sa Covid šifrom..."
- "Zaustavljanje lanca širenja infekcije"
+ "Upozorenje u slučaju moguće infekcije""Unos Covid šifre"
@@ -227,7 +227,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Praćenje je deaktivirano""Aktivirajte praćenje da bi aplikacija radila.""Bluetooth isključen"
- "Uključite Bluetooth na vašem uređaju da bi praćenje radilo."
+ "Uključite Bluetooth na vašem uređaju, kako bi funkcionisalo anonomno praćenje.""Uključi Bluetooth""Korišćenje Bluetooth-a odbijeno""Dozvolite korišćenje Bluetooth-a da bi praćenje radilo."
@@ -259,8 +259,8 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Sledite uputstva za izolaciju koja ste dobili"
- "Praćenje završeno"
- "Dalji kontakti se više ne čuvaju."
+ "Deaktivirani kontakti i prijave "
+ "Dalji kontakti se više ne čuvaju""Obratite se""Sledite""Uputstva za samoizolaciju"
@@ -273,7 +273,7 @@ Time nam pomažete u prekidanju lanca širenja infekcije."
"Testiran pozitivno"
- "Postoji mogućnost infekcije."
+ "Postoji mogućnost infekcije""Testirani ste pozitivno na virus korona."
@@ -330,7 +330,7 @@ ispred virusa"
"Pomoću aplikacije Swiss Covid svi možemo da pomognemo u suzbijanju širenja virusa korona."
- "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korona."
+ "Aplikacija vas obaveštava ako postoji sumnja na to da ste zaraženi virusom korone.""Privacy by Design"
@@ -345,7 +345,7 @@ ispred virusa"
"Sačuvani nasumični ID-ovi se čuvaju na vašem mobilnom telefonu i brišu se nakon 14 dana."
- "Kako funkcioniše aplikacija?"
+ "Kontakti""Prepoznavanje kontakta
@@ -421,7 +421,7 @@ Pridržavajte se dobijenih uputstava o izolaciji.""Aplikacija je spremna"
- "Hvala vam što pomažete u prekidanju lanca širenja infekcije."
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone.""Pokreni"
@@ -448,7 +448,7 @@ Pridržavajte se dobijenih uputstava o izolaciji."
"Moguća infekcija"
- "Postoji sumnja na moguću infekciju."
+ "Postoji sumnja da ste se inficirali.""Vremenska greška"
@@ -584,7 +584,7 @@ Time se potrošnja baterije samo neznatno povećava."
"Šta su poruke?"
- "Aplikacija redovno proverava da li su drugi korisnici sa kojima ste bili u kontaktu prijavili pozitivan test.
+ "Aplikacija redovno proverava da li su drugi korisnici, sa kojima ste bili u kontaktu, prijavili pozitivan test.
U slučaju moguće infekcije, dobićete obaveštenje na zaključanom ekranu."
@@ -625,7 +625,7 @@ Time se obezbeđuje da samo potvrđeni slučajevi budu prijavljeni preko aplikac
"Šta se šalje?"
- "Šalju se samo privatni identifikacioni ključevi iz vaše aplikacije, a ne šalju se lični podaci.
+ "Šalju se samo privatni ključevi iz vaše aplikacije i šifrovani ID podaci vaših prijava, a ne šalju se lični podaci.
Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost infekcije."
@@ -636,7 +636,7 @@ Time drugi korisnici aplikacije SwissCovid mogu da saznaju da postoji mogućnost
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Važna napomena"
+ "Informacije za korišćenje ""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma."
@@ -710,7 +710,7 @@ Osim toga, mogući su sledeći simptomi:
"Aktiviraj praćenje"
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""Aktiviraj"
@@ -739,10 +739,10 @@ Osim toga, mogući su sledeći simptomi:
"Da li želite da završite fazu izolacije?"
-
+
"Da li ste sigurni da želite da obrišete poruku?""Ignoriši optimizaciju baterije"
- "Da biste uvek mogli da prepoznate kontakte, optimizacija baterije za ovu aplikaciju mora biti isključena."
+ "Kako bi aplikacija funkcionisala, optimizacija baterije mora biti isključena.""Dozvoli""OK""Samo za ovu aplikaciju"
@@ -764,7 +764,7 @@ Osim toga, mogući su sledeći simptomi:
"COVID-19: obavještenja deaktivirano"
- "Aktivirajte Obavijesti o izloženosti bolesti COVID-19 da bi praćenje radilo."
+ "Aktivirajte funkciju {TRACING_SETTING_TEXT}, kako bi funkcionisalo anonimno praćenje.""Slanje poruke nije uspelo. Dozvolite deljenje nasumičnog ID-a sa aplikacijom SwissCovid."
@@ -806,9 +806,8 @@ Ako trenutno na uređaju nije dostupno ažuriranje, probajte kasnije ponovo.""Aplikacija SwissCovid ostaje aktivna čak i kad se zatvori."
- "SwissCovid
-prekida lanac
-širenja infekcije."
+ "SwissCovid - brze informacije u slučaju rizika od zaraze.
+""Iako se lični podaci koji se odnose na vas ne šalju, možda se neko seća svog kontakta sa vama na osnovu datuma.""Da li ostajem anoniman?"
@@ -819,7 +818,7 @@ prekida lanac
"Obavijesti o izloženosti bolesti COVID-19"
- "Da biste mogli da koristite aplikaciju, morate da aktivirate obavijesti o izloženosti bolesti COVID-19."
+ "Za identifikaciju kontakata, mora biti aktivirana funkcija praćenja.""SwissCovid"
@@ -912,7 +911,7 @@ Schwarzenburgstrasse 157
"Putovanja"
- "SwissCovid funkcijoniše u sledećim državama:"
+ "SwissCovid-kontakti funkcionišu u sledećim državama:""Šta to znači?"
@@ -942,12 +941,6 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1229,9 +1222,9 @@ Da bi putnici u inostranstvu mogli da se upozore, to se dešava nezavisno od tog
"Beenden (završi)"
- "Kontakti više neće da se čuvaju.
+ "Kontakti se više neće čuvati i nove prijave nisu više moguće.
-Praćenje može ponovo da se aktivira po završetku izolacije."
+Ove dve funkcije mogu se ponovo aktivirati po završetku izolacije.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1263,7 +1256,7 @@ Praćenje može ponovo da se aktivira po završetku izolacije.""https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "Da li ste bez sluha ili imate smetnju sluha i ne možete nazvati Infoline Coronavirus?
+ "Da li ste bez sluha ili imate oštećenje sluha i ne možete nazvati?
Onda pošaljite e-mail na covid-support@medgate.ch"
@@ -1318,8 +1311,8 @@ Vreme je odlučujući faktor u suzbijanju Corona virusa: što je vreme između p
"Grafika pokazuje prijavljene nove infekcije poslednjih 28 dana. Time imate pregled o aktuelnom razvoju."
- "Pozitivno ste testirani (PCR test ili antigen brzi test) i ni posle 4 sata niste dobili kovid šifru?
-U tom slučaju kontaktirajte Infoline Coronavirus:"
+ "Pozitivno ste testirani (PCR test ili brzi antigen test) i ni posle 4 sata niste dobili Covid kod?
+U tom slučaju kontaktirajte nas ovde:""Još niste dobili Covid šifru?"
@@ -1358,13 +1351,13 @@ U tom slučaju kontaktirajte Infoline Coronavirus:""Ko se obaveštava"
- "Obaveštavaju se svi kontakti u periodu vaše faze zaražavanja od {ONSET_DATE} do unosa kovid šifre, ukoliko je na osnovu distance i trajanja postojala mogućnost zaraze."
+ "Obaveštavaju se svi kontakti u periodu vaše faze infekcioznosti od {ONSET_DATE} do unosa Covid koda, ukoliko je na osnovu distance i trajanja ili zbog prijave na istoj lokaciji postojala mogućnost zaraze.""{ONSET_DATE} – danas"
- "Na taj način pomažete u zaustavljanju lanca zaraze."
+ "Na taj način pomažete da se upozore drugi o mogućosti zaraze.""Privatni ključevi vaše aplikacije poslati su za sledeći period:"
@@ -1403,363 +1396,359 @@ U tom slučaju kontaktirajte Infoline Coronavirus:"
"Za 1 dan"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Do you want to check out?"
+ "Želite li da se odjavite?"
- "Automatic check-out"
+ "Automatska odjava"
- "You were checked out automatically."
+ "Automatski ste odjavljeni."
- "Scan QR code and check in"
+ "Skenirati QR kod i prijaviti se"
- "No valid QR code!"
+ "Nevažeći QR kod!"
- "From"
+ "Od"
- "To"
+ "Do"
- "Add note"
+ "Dodati zabelešku"
- "e.g. who were you with?"
+ "npr. S kim ste bili tamo?"
- "Done"
+ "Gotovo"
- "Check-out reminder"
+ "Podsećanje na prijavu"
- "Off"
+ "Isključeno"
- "Authentication needed to access the diary"
+ "Identifikujte se, kako biste pristupili dnevniku"
- "{HOURS} h"
+ "časovi {HOURS} h"
- "{MINUTES}'"
+ "minute {MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "Aplikaciji je potreban pristup kameri, da bi se skenirao QR kod."
- "My check-ins"
+ "Moje prijave"
- "Check-in"
+ "Prijava"
- "The camera is needed to scan the QR code."
+ "Kamera je potrebna, kako bi se skenirao QR kod."
- "Allow Face ID to securely access your diary."
+ "Dozvolite identifikaciju lica, da biste mogli bezbedno pristupiti vašem dnevniku."
- "Authentication needed to access the diary"
+ "Identifikujte se za pristup dnevniku"
- "Check-out"
+ "Odjava "
- "Check-in"
+ "Prijava"
- "Check-in"
+ "Prijava"
- "Generate QR code"
+ "Napraviti QR kod"
- "Offer check-ins?"
+ "Ponuditi prijave?"
- "Check in now"
+ "Sada se prijaviti"
- "The reminder could not be set"
+ "Podsećanje nije uspelo da se unese"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Da biste dobijali podsetnike za odjavu, morate da dozvolite \ \"Obaveštenja \\" u podešavanjima aplikacije."
- "Customise"
+ "Prilagoditi"
- "Edit"
+ "Urediti"
- "No entries available yet"
+ "Još nema unosa"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate SwissCovid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid-u treba pristup kameri na vašem aparatu. Kamera se koristi, kako bi se skenirao QR kod."
- "Allow access to camera"
+ "Dozvoliti pristup kameri"
- "e.g. room or event name"
+ "npr. soba ili naziv događaja"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
- "Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Odštampati PDF"
+ "Informacija"
+ "Mrežna greška"
+ "Proverite vašu vezu sa internetom "
+ "Pokušajte ponovo"
+ "Deaktivirati obaveštenja"
+ "Promenite podešavanja, kako biste bili direktno bili obavešteni o prijavi."
+ "Promenite podešavanja"
+ "Nema pristupa kameri"
+ "Aplikaciji je potreban pristup kameri, kako bi se mogao skenirati QR kod."
+ "Greška"
+ "QR kod je još nevažeći"
+ "QR kod je nevažeći "
+ "Upravo ste prijavljeni."
+ "Ovaj QR kod nije kompatibilan sa instaliranom verzijom aplikacije. Ažurirajte aplikaciju, da biste nastavili korišćenje."
+ "Potrebno ažuriranje"
+ "Sada ažurirati"
+ "Odjava "
+ "Snooze mirovanje"
+ "Poruka o trajnom prijavljivanju"
+ "Prijavljeni ste {TIME} časova."
- "Save"
+ "Osigurati"
- "Scan QR code"
+ "Skenirati QR kod"
- "Check-in deactivated"
+ "Deaktivirano prijavljivanje"
- "Check-in is not possible at the moment"
+ "Trenutno nije moguća prijava"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Ako skenirate Swiss-Covid-QR kod, da biste se prijavili na nekoj lokaciji, ovde se automatski uređuje unos."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Prijavljeni ste"
+ "{DATE1} do {DATE2}"
+ "Više o Covid kodu"
- "Remove entry"
+ "Ukloniti unos"
- "Delete"
+ "Izbrisati"
- "Share"
+ "Podeliti"
- "Print"
+ "Štampati"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na nekoj lokaciji."
- "Share check-in entries"
+ "Deliti unose o prijavama"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Kada delite unose prijave, aplikacija može istovremeno da obavesti lica, koja su se prijavila na istoj lokaciji."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Izabrati sve"
+ "Ne slati"
+ "Ne deliti"
+ "Zaista ne deliti?"
+ "Deljenje privatnog ključa pomaže «SvissCovid» da obavesti druge potencijalno zaražene ljude i spreči dalje širenje virusa. Ako ne delite privatne ključeve, potencijalno zaražene osobe neće biti obaveštene."
- "Remove entry"
+ "Ukloniti unos"
- "Your entry will be permanently removed from the list."
+ "Vaš unos će biti neopozivo uklonjen sa liste."
- "Completely remove"
+ "Ukoniti u potpunosti"
- "Only hide and keep receiving notifications?"
+ "Samo sakriti i nastaviti da budete obaveštavani?"
- "Tested positive?"
+ "Test pozitivan?"
- "Reactivate encounters and check-in"
+ "Kontakte i prijave ponovo aktivirati. "
- "{HOURS} h {MINUTES}'"
+ "časovi {HOURS} h minute {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Ne možete ponovo da se prijavite ako imate pozitivan test."
- "Generate a QR code that people can use to check in on site."
+ "Napravite QR kod, pomoću kojeg lica mogu da se prijave na lokaciji."
- "Are you sure you want to delete the QR code?"
+ "Želite li zaista da izbrišete QR kod? "
- "Self check-in"
+ "Samostalno prijavljivanje"
- "Scan a SwissCovid QR code to check in on site."
+ "Skenirajte SwissCovid-QR kod, kako biste se prijavili na lokaciji."
- "Saving not possible"
+ "Nije moguće obezbeđenje"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Odabrana vremena se preklapaju sa ostalim prijavama iz vašeg dnevnika. Molimo vas da ponovo proverite svoje podatke."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Vaši odabrani unosi za prijavu su poslati. Biće obaveštena sva lica koja su se istovremeno prijavili na istom mestu."
- "SwissCovid check-in"
+ "SwissCovid prijava
+
+"
- "Check in without leaving any data"
+ "Prijaviti se bez ostavljanja podataka"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "Pomoći aplikacije SwissCovid možete jednostavno da se prijavite kod nas ne ostavljajući svoje podatke. U slučaju rizične situacije, brzo ćete biti obavešteni u aplikaciji."
- "SwissCovid app"
+ "SwissCovid aplikacija"
- "Download now"
+ "Sada preuzeti"
- "Check in without leaving
-data"
+ "Prijaviti se bez ostavljanja podataka"
- "Enter your Covidcode here to alert others."
+ "Ovde unesite svoj Covid kod, kako biste upozorili druge."
- "Isolation ended?"
+ "Kraj izolacije?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Želite da ponudite prijave?"
+ "Čemu služi funkcija prijave?"
+ "Funkcija prijave bez unošenja podataka o kontaktima povećava funkcije aplikacije SwissCovid. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istom mestu."
+ "Nema centralnog čuvanja podataka"
+ "Za SwissCovid prijave ne treba unositi lične podatke kao što su ime, adresa ili telefonski broj i ne beleže se GPS podaci o lokaciji. Spisak prijava čuva se lokalno na vašem mobilnom telefonu. Lokalni podaci će se izbrisati nakon 14 dana."
+ "Napravite QR kod"
+ "Sa SwissCovid-QR kodom mogu se na licu mjesta prijaviti lica, a da ne ostavljaju svoje podatke. "
+ "To nije zamena za obavezni unos kontakt podataka"
+ "SwissCovid prijave nisu zamena za obavezu evidenciju kontakt podataka utvrđenu u „Pravilniku Covid 19 za posebne situacije“."
+ "Gde se mogu koristiti prijave?"
+ "Zašto služi funkcija prijave?"
+ "SwissCovid prijave se mogu koristiti tamo gde se ljudi sastaju, ali ne postoji obaveza beleženja podataka o kontaktima. Moguće lokacije:
+- Privatni događaji
+- Događaji u klubovima
+- Sastanci u profesionalnom okruženju kao što su sale za sastanke, kancelarije, menze itd. \""
+ "Funkcija prijave bez unošenja podataka o kontaktima proširuje funkcije aplikacije. Pomoću ove funkcije, u slučaju pozitivnog testa, možete ne samo da upozorite bliske kontakte, već i lica koja su se istovremeno prijavila na istoj lokaciji."
- "Risk situation"
+ "Rizična situacija"
- "Encounters"
+ "Kontakti"
- "Check-in"
+ "Prijava"
- "What should I do?"
+ "Šta treba da uradima?"
- "Positive check-in"
+ "Pozitivna prijava"
- "Positive check-ins/contacts"
+ "Pozitivne prijave / kontakti"
- "User QR code"
+ "QR kod korisnika"
- "Contact tracing QR code"
+ "QR kod za praćenje kontakata"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Ako je rezultat testa pozitivan, funkcija prijave se automatski deaktivira. Može se ponovo aktivirati nakon završetka izolacije."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Dodelite naziv, koji detaljnije opisuje lokaciju ili vaš događaj. Ovaj naziv će biti prikazan prilikom prijave."
+ "Šta možete da uradite?"
+ "Zaštititi kontakte"
+ "Obratiti pažnju na simptome"
+ "Vršiti testiranje"
+ "Mogli biste biti zarazni, a da to ne primetite. Pridržavajte se važećih higijenskih i zaštitnih mera i zaštitite svoju porodicu, prijatelje i okolinu."
+ "Proveravajte svoje zdravsveno stanje."
+ "Odmah se testirajte ako imate simptome. Čak i ako nema simptoma, test korone je važan i preporučuje se."
- "Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Prijava"
+ "Podaci nisu poslati"
+ "Nisu poslati privatni ključevi."
+ "Nazad"
+ "Ne slati nikakve podatke"
+ "Nisu poslati unosi za prijavu."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
- "Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Deliti izbor"
+ "Ne deliti"
+ "Prijaviti se bez unošenja podataka"
+ "Prijave"
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka."
+ "Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji."
+ "Sa SwissCovid prijavama možete da koristite aplikaciju za prijavu na nekoj lokaciji ili prilikom sastanka. Vaše prisustvo se memoriše samo u aplikaciji. Ne morate da ostavljate lične podatke u aplikaciji. U ove svrhe ažurirani su uslovi korišćenja i izjava o zaštiti podataka."
+ "Preskočiti"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "Šifrovani unos čuva se u pozadini aplikacije 14 dana. U slučaju moguće infekcije, i dalje možete biti obavešteni."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Sada sakriti"
+ "Aplikacija vas može podsetiti da se odjavite u pravo vreme."
- "Or remove completely?"
+ "Ili potpuno ukloniti?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "Unos je potpuno uklonjen. Više ne možete biti upozoreni."
+ "Niko neće biti obavešten."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "Šta treba da znam o testiranju?"
+ "Instalirati aplikaciju i prijavite se"
+ "Završite konfiguraciju aplikacije, kako biste mogli da koristite funkciju praćenja."
+ "Završiti sada"
+ "Konfiguracija završena"
+ "Hvala vam što pomažete u suzbijanju širenja virusa korone."
+ "Brze informacije u slučaju rizika od zaraze. ""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 4a072b544..db660c206 100644
--- a/common/src/main/res/values-ti/strings.xml
+++ b/common/src/main/res/values-ti/strings.xml
@@ -103,7 +103,7 @@
"ብሕማም ናይ ምልካፍ ተኽእሎ"
- "ብሕማም ናይ ምልካፍ ተኽእሎ ኣሎ።"
+ "ልበዳ ተለኪፍኩም ትኾኑ""ኣፕ ይሰርሕ ኣሎ"
@@ -193,7 +193,7 @@
"ብኮቪድኮድ"
- "ሰንሰለት ለበዳ ሕማም ምቁራጽ"
+ "ካብ ክፍጸሙ ዝኽእሉ ልበዳታት ምጥንቃቕ""ኮቪድኮድ ኣእትው"
@@ -227,7 +227,7 @@
"ኣገልግሎት ምክትታል ኣይሰርሕን ኣሎ (ጠፊኡ ኣሎ)""እቲ ኣፕ ክሰርሕ ምእንታን ነቲ ኣገልግሎት ምክትታል ከም ዝሰርሕ ግበርዎ (ወልዕዎ)። ""ብሉቱዝ ጠፊኡ ኣሎ"
- "ኣገልግሎት ምክትታል ክስርሕ ምእንታን ኣብ ሞባይልኩም ብሉቱዝ ክፈቱ።"
+ "ኣገልግሎት ስመ-ስውር ምክትታል ክስርሕ ምእንታን ኣብ ሞባይልኩም ብሉቱዝ ክፈቱ።""ብሉቱዝ ክፈትዎ""ምጥቃም ብሉቱዝ ተነጺጉ""ኣገልግሎት ምክትታል ክስርሕ ምእንታን ብሉቱዝ ምጥቃም ኣፍቅዱ። "
@@ -259,11 +259,11 @@
"ንምግላል ዝምልከት ዝተወሃበኩም መምርሒታት ተኸታተሉ። "
- "ምክትታል ተወዲኡ"
- "ካብ ሕጂ ንንዮ ርክባት ኣይዕቀቡን እዮም። "
+ "ምርኽኻብን ቸክ-ኢንን ተዓጽዩ ኣሎ"
+ "ዝኾነ ምርኽኻብ ኣይክዕቀብን እዩ""ን…..ተወከሱ""ን……..ተኸታተሉ"
- "ባዕልኻ ነብስኻ ንምግላል ዝምልከት መምርሒታት "
+ "ንምግላል ዝምልከት መምርሒታት""ሓድሽ ሓበሬታ"
@@ -273,7 +273,7 @@
"ውጽኢት መርመራ ፖዚቲቭ ኮይኑ"
- "ተኽእሎ ናይ ለበዳ ሕማም ኣሎ። "
+ "ልበዳ ተለኪፍኩም ትኾኑ""ዝገበርካዮ መርመራ ንኮሮናቫይረስ ውጽኢቱ ፖዚቲቭ ኮይኑ ተረኺቡ።"
@@ -342,7 +342,7 @@
"እቶም ዝተዓቀቡ ብግምት ዝተሰርዑ መለልዪ ቁጽርታት ኣብ ሞባይልኩም ጸኒሖም ድሕሪ 14 መዓልቲ ይስረዙ።"
- "እቲ ኣፕ ከምይ ይሰርሕ፧"
+ "ምርኽኻብ""ሳላ ብሉቱዝ ርክባት ምልላይ "
@@ -414,7 +414,7 @@
"እቲ ኣፕ ተዳልዩ ኣሎ።"
- "ሰንሰለት ለበዳ ሕማም ንምቁራጽ ምትሕግጋዝኩም ኣዚና ነመስግነኩም።"
+ "ምዝርጋሕ ኮሮናቫይረስ ንምድራት እትገብሩ ምትሕብባር ነመስግነኩም ኢና።""ምጅማር"
@@ -441,7 +441,7 @@
"ከምብሓድሽ ጥርጠራ"
- "ተኽእሎ ናይ ምልካፍ ብሕማም ኣሎ።"
+ "ልበዳ ተለኪፍኩም ትኾኑ""ናይ ሰዓት ጌጋ"
@@ -573,8 +573,9 @@
"ሓበሬታ ክብሃል ከሎ እንታይ ማለት እዩ፧"
- "እቲ ኣፕ ክልኦት ዝተራኸብኩሞም ተጠቀምቲ ብመርመራ ፖዚቲቭ ከምዝኾኑ ሓቢሮም እንተኾይኖም ቀጻሊ ይምርምር።
-ተኽእሎ ምልካፍ ብሕማም ምስዝህሉ ብመነግዲ መልእኽቲ ኣብ ዕጹው ስክሪን ሞባይል ክትሕበሩ ኢኹም። "
+ "እቲ ኣፕ ክልኦት ዝተራኸብኩሞም ተጠቀምቲ ብመርመራ ፖዚቲቭ ከምዝኾኑ ሓቢሮም እንተኾይኖም ቀጻሊ ይምርምር።
+
+ኣብ ክፍጸም ዝኽእል ልበዳ ብመልእኽቲ ኣብ መዕጸዊ ስክሪን ክትሕበሩ ኢኹም።""መስመር ሓበሬታ ስዊስኮቪድ ብዛዕባ ተኽእሎ ካብ ክፍሊት ነጻ ናይ ዝኾነ መርመራ የማኽረኩም። "
@@ -611,8 +612,8 @@
"ውልቃዊ ሓበሬታ ዘይኮነ፣ ጥራይ እቶም ግላውያን መፋትሕ እዮም ዝስደዱ።
-
-ብኸምዚ መንገዲ ካልኦት ተጠቀምቲ ስዊስኮቪድ ተኽእሎ መልከፍቲ ሕማም ከምዘሎ ይፈልጡ።"
+
+ተኽእሎ ናይ ምልከፍቲ ከምዘሎ እቶም ተጠቀምቲ ስዊስ ኮቪድ ኣፕ (SwissCovid App) ክፈልጡ ይኽእሉ።"
"+41 58 387 77 78"
@@ -621,10 +622,10 @@
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "ኣገዳሲ ሓበሬታ"
+ "ሓበሬታ ብዛዕባ ምእትታው"
- "ዝኾነ ሓበሬታ ብዛዕባኹም ዋላኳ እንተዘይተሰደደ: ዝኾነ ሰብ በቲ ዕለት ጌሩ ምርኻብኩም ክዝክሮ ይኽእል እዩ። "
+ "ዝኾነ ሓበሬታ ብዛዕባኹም ዋላኳ እንተዘይተሰደደ: ዝኾነ ሰብ በቲ ዕለት ጌሩ ምርኻብኩም ክዝክሮ ይኽእል እዩ።""ተሰማሚዐ"
@@ -696,7 +697,7 @@
"ኣገልግሎት ምክትታል ከም ዝሰርሕ ግበርዎ (ወልዕዎ)"
- "ኣፕ ምጥቃም ንምኽኣል ናይ ኮቪድ-19 መልእኽትታት ናይ ርክባት ከጅመሩ (ከም ዝሰርሑ ክግበሩ) ኣለዎም።"
+ "እቲ ናይ ምክትታል ኣገባብ ክውላዕ ኣለዎ፣ ምርኽኻብ ክረጋገጽ መታን ክከኣል።""ምውላዕ (ከም ዝሰርሕ ምግባር)"
@@ -725,10 +726,10 @@
"ግለላ ምዝዛም"
-
+
"ሓበሬታ ናይ ብሓቂ ክትድምስስዎ ደሊኹም ዶ፧""ኣገልግሎት ምምሕያሽ ባትሪ (ኣኩ) ሸለል ምባል"
- "ርክባት ኩልግዜ ምልላይ ንምኽኣል: ኣገልግሎት ምምሕያሽ ባትሪ (ኣኩ)ናይዚ ኣፕ ዕጹው ክኸውን ኣለዎ። "
+ "ዝኾነ ምቍጣብ ባትሪ ዘልዕል ኣገባብ ኣብቲ ሞባይል ክጠፍእ ኣለዎ፣ እቲ ኤፕ መታን ጽቡቕ ክሰርሕ።""ምፍቃድ (ሐራይ ምባል)""ሐራይ""ነዚ ኣፕ ጥራይ"
@@ -750,7 +751,7 @@
"መልእኽትታት ርክባት ኮቪድ-19 ኣይሰርሕን ኣሎ (ዕጹው ኣሎ) "
- "ምክትታል ክሰርሕ ምእንቲ መልእኽትታት ርክባት ኮቪድ-19 ከምዝሰርሕ ግበርዎ (ወልዕዎ)። "
+ "ን {TRACING_SETTING_TEXT} ወልዕዎ፣ እቲ ስመ-ስውር ምክትታል ክሰርሕ ምእንቲ ክኽእል።""መልእኽቲ ክስደድ ኣይክኣለን። ብግምት ዝተሰርዑ መለልዪ ቁጽርታትኩም ብስዊስኮቪድ ኣፕ ንኽከፋፈል ኣፍቅዱ። "
@@ -791,7 +792,8 @@
"እቲ ኣፕ ዋላ እንተተዓጽወ ስዊስኮቪድ ይስርሕ እዩ።"
- "ስዊስኮቪድ ንሰነሰለት ለበዳ ሕማም የቋርጾ"
+ "SwissCovid -
+ኣብ ርከሳ ልበዳ ብቕልጡፍ ትሕበሩ።""ዋላኳ ዝኾነ ሓበሬታ ብዛዕባኹም እንተዘይተሰደደ ዝኾነ ሰብ በቲ ዕለት ጌሩ ምርኻብኩም ክዝክሮ ይኽእል እዩ። ""ስቱር ድዩ፧"
@@ -802,7 +804,7 @@
" መልእኽትታት ኮቪድ-19 "
- "ኣፕ ምጥቃም ንኽክኣል መልእኽትታት ናይ ኮቪድ-19 ክኽፈቱ ወይ ከም ዝሰርሑ ክግብሩ ኣለዎም።"
+ "እቲ ናይ ምክትታል ኣገባብ ክውላዕ ኣለዎ፣ ምርኽኻብ ክረጋገጽ መታን ክከኣል።""ስዊዝኮቪድ"
@@ -895,7 +897,7 @@ Schwarzenburgstrasse 157
"መገሻታት"
- "SwissCovid ኣብ ዝስዕቡ ሃገራት ይሰርሕ፥"
+ "ምርኽኻብ SwissCovid ኣብ ዝስዕቡ ሃገራት ይሰርሕ፥""እዚ እንታይ ማለት፧"
@@ -925,12 +927,6 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1212,9 +1208,9 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ተዛዘመ"
- "ምዕቃብ ዝኾነ ምርኽኻብ ተገዲፉ።
+ "ዝኾነ ሓድሽ ምርኽኻብ ካብ ምዕቃብ ክቋረጽ እዩ ከምኡ ድማ ሓድሽ ቸክ-ኢን ክግበር ኣይክእልን።
-እቲ ምክትታል ድሕሪ ግለላ ምሊሱ ክውላዕ ይከኣል።"
+እቲ ግለላ ምስተወድአ ክልተኦም ኣገባባት እንደገና ክውላዑ ይከኣሉ።""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1246,7 +1242,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "ክትሰምዑ ኣይትኽእሉን ወይ ናይ ምስማዕ ስንክልና ስለዘለና ነቲ ሓበሬታ ስልኪ ኮሮናቫይረስ ክትድውሉ ኣይትኽእሉን፧
+ "ክትሰምዑ ኣይትኽእሉን ወይ ናይ ምስማዕ ስንክልና ኣለኩም ከምኡ ድማ ክትድውሉ ኣይትኽእሉን፧
ሽዑ ኢመይል ናብ covid-support@medgate.ch ስደዱ ኢኹም"
@@ -1301,7 +1297,8 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"እቲ ስእሊ ነቶም ዝተሓበሩ ሓድሽቲ ልበዳታት ኣብ ዝሓለፉ 28 መዓልትታት የርኢ። እዚ ሓፈሻዊ ሓበሬታ ብዛዕባ ግዝያዊ ምዕባለ ይህብ።"
- "ፖዚቲቭ ተመርሚርኩም (ናይ PCR መርመራ ወይ ቅልጡፍ መርመራ ጸረ-ፈርዒ) ከምኡ'ውን ድሕሪ 4 ሰዓት ገና ናይ ኮቪድ ኮድ ኣይተወሃብኩምን፧ ሽዑ እቲ ኢንፎላይን ኮሮናቫይረስ ርኸቡ ኢኹም፥"
+ "ፖዚቲቭ ተመርሚርኩም (ናይ PCR መርመራ ወይ ቅልጡፍ መርመራ ጸረ-ፈርዒ) ከምኡ'ውን ድሕሪ 4 ሰዓት ገና ናይ ኮቪድ ኮድ ኣይተወሃብኩምን፧
+ሽዑ ናብዚ ደውሉ ኢኹም፥""ናይ ኮቪድ ኮድ ገና የብልኩምን፧"
@@ -1325,7 +1322,7 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ተቓደውቲ ሃገራት፥"
- "ገየሽቲ ካብ ወጻኢ ውን መታን ክጥንቀቑ፣ እቶም ግላውያን መፋትሕ ምስቶም ኮሮና ኤፕስ ናይቶም ተቓደውቲ ሃገራት ክካፈሉ እዮም።"
+ "ገየሽቲ ካብ ወጻኢ ውን ድሕሪ ምርኽኻብ መጠንቀቕታ መታን ክወሃቡ፣ እቶም ግላውያን መፋትሕ ምስቶም ኮሮና ኤፕስ ናይቶም ተቓደውቲ ሃገራት ክካፈሉ እዮም።""ጥቕላላ"
@@ -1340,13 +1337,13 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"መን ክሕበር እዩ፧"
- "ኩሎም ኣብ እዋንኩም መልከፍቲ ካብ {ONSET_DATE} ክሳብ ምእታው ኮቪድ-ኮድ ዝነበረኩም ርክባት ክሕበሩ እዮም፣ ብምኽንያት ርሕቐትን እዋንን ኣማራጺ ልበዳ እንድሕር ኔሩ።"
+ "ኩሎም ኣብ እዋንኩም መልከፍቲ ካብ {ONSET_DATE} ክሳብ ምእታው ኮቪድ-ኮድ ዝነበረኩም ርክባት ክሕበሩ እዮም፣ ብምኽንያት ርሕቐትን እዋንን ናይ ልበዳ ርከሳ እንድሕር ኔሩ ወይ ኣብ ማዕረ ቦታ ብችክ-ኢን እድንድሕር ተመዝጊብኩም ኔርኩም።""{ONSET_DATE} – ሎሚ"
- "ሽዑ ናይ ልበዳ ሰንሰለታት ንክቋረጹ ትሐባበሩ ኢኹም።"
+ "በዚ መንገዲ‘ዚ ካልኦት ሰባት ካብ ክፍጸም ዝኽእል ልበዳ ንክጥንቀቑ ትሕግዙ ኣለኹ።""ግላውያን መፋትሕ ናይ ኤፕኩም ንዝስዕብ እዋን ተሰዲዶም ኔሮም፥"
@@ -1385,46 +1382,46 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"ኣብ 1 መዓልቲ"
- "Check-out reminder"
+ "ናይ ቸክ-ኣውት/መውጽኢ መዘኻኸሪ"
- "Do you want to check out?"
+ "ክትወጹ ደሊኹም ዶ፧"
- "Automatic check-out"
+ "ኣውቶማቲካሊ ቸክ-ኣውት"
- "You were checked out automatically."
+ "ብኣውቶማቲካሊ ከም ትወጹ ተገሩ።"
- "Scan QR code and check in"
+ "ኮድ Code ስኬን ምግባርን ምእታውን"
- "No valid QR code!"
+ "ብቑዕ QR-ኮድ ኣይኮነን!"
- "From"
+ "ካብ"
- "To"
+ "ክሳብ"
- "Add note"
+ "ምልክታ ምውሳኽ"
- "e.g. who were you with?"
+ "ንኣ. ምስ መን ኔርኩም፧"
- "Done"
+ "ተፈጺሙ"
- "Check-out reminder"
+ "ናይ ቸክ-ኣውት/መውጽኢ መዘኻኸሪ"
- "Off"
+ "ወጽአ"
- "Authentication needed to access the diary"
+ "መዝገበ-ዕለት ንክትረኽቡ ፍቓድ መእተዊ ኣምጽኡ።""{HOURS} h"
@@ -1433,315 +1430,313 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"{MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "እቲ ኤፕ ርክብ ምስቲ ካመራ ክህልዎ ኣለዎ፣ ነቲ ኮድ QR ስኬን ክገብር መታን ክኽእል።"
- "My check-ins"
+ "ናተይ ቸክ-ኢን"
- "Check-in"
+ "ቸክ-ኢን"
- "The camera is needed to scan the QR code."
+ "ካሜራ ነቲ ኮድ QR ስኬን ንክትገብር፣ ተድልያ።"
- "Allow Face ID to securely access your diary."
+ "ነቲ ናይ ገጽ ኣይ.ዲ. ኣፍቅዱ፣ ንመዝገበ-ዕለትኩም ብርጉጽ ንክትረኽብዎ።"
- "Authentication needed to access the diary"
+ "መዝገበ-ዕለት ንክትረኽቡ ፍቓድ መእተዊ ኣምጽኡ።"
- "Check-out"
+ "ቸክ-ኣውት"
- "Check-in"
+ "ቸክ-ኢን"
- "Check-in"
+ "ቸክ-ኢን"
- "Generate QR code"
+ "QR-ኮድ ምፍጣር"
- "Offer check-ins?"
+ "ቸክ-ኢን ምቕራብ"
- "Check in now"
+ "ሕጂ ቸክ-ኢን ምእታው"
- "The reminder could not be set"
+ "እቲ መዘኻኸሪ ክዕቀብ ኣይከኣለን"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "ናይ ቸክ-ኣውት መዘኻኸሪታት መታን ክትቕበሉ \\"Mitteilungen/መልእኽትታት\\" ኣብቲ ምስትኽኽእል ኤፕ ከተፍቕዱ ኣለኩም።"
- "Customise"
+ "ምስናድ"
- "Edit"
+ "ምስራሕ"
- "No entries available yet"
+ "ገና መዝገባት የለውን"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "ናይ ስዊዝ ኮቪድ ኮድ QR ኣብቲ ቦታ ንክትኣትዉ ስከን እንተገበርክኩም፣ ኣውቶማቲካሊ መዝገብ ክዕቀብ እዩ።"
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid ምምርኽኻብ ምስ ካመራ ኣብ መሳርሒኹም የድሊ። እታ ካሜራ ጥራይ ኮድ QR ስኬን ንምግባር ትጥቀም እያ።"
- "Allow access to camera"
+ "መርከቢ ካመራ ምፍቓድ"
- "e.g. room or event name"
+ "ንኣ. ክፍሊ ወይ ቦታ ፍጻሜ"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
- "Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "PDF ፕሪንት ምግባር"
+ "ሓበሬታ"
+ "ጉድለት መርበብ"
+ "ምትእስሳር ኢንተርነትኩም ኣረጋግጹ ኢኹም"
+ "ብሓድሽ ምፍታን"
+ "መልእኽትታት ጠፊኦም"
+ "ኣብ ሓደ መልእኽቲ ብቐጥታ መታን ክትሕበሩ ነቲ ምስትኽኻል ቀይሩ ኢኹም።"
+ "ምስትኽኻል ምቕያር"
+ "ካመራ ክትርከብ ኣይከኣለትን"
+ "እቲ ኤፕ ርክብ ምስቲ ካመራ ክህልዎ ኣለዎ፣ ነቲ ኮድ QR ስኬን ክገብር መታን ክኽእል።"
+ "ጉድለት"
+ "QR-ኮድ ገና ብቑዕ ኣይኮነን"
+ "ብቕዓት QR-Code ሓሊፉ"
+ "ድሮ ኣቲኹም ዲኹም፧"
+ "እዚ ኮድ QR ምስቲ ዝተተከለ ቨርዝዮን ኤፕ ኣይሰርሕን እዩ። ነቲ ኤፕ ኣሕድስዎ ብኽብረትኩም መታን ክትቕጸሉ።"
+ "ምምሕዳስ የድሊ"
+ "ሕጂ ምምሕዳስ"
+ "ቸክ-ኣውት"
+ "ስኑዝ"
+ "ዝተደጋጋሚ መሓበሪ ቸክ-ኢን"
+ "ኣቲኹም ኢኹም ካብ ሰዓት {TIME}።"
- "Save"
+ "ምርግጋጽ"
- "Scan QR code"
+ "QR-ኮድ ስከን ምግባር"
- "Check-in deactivated"
+ "Check-In ተዓጽዩ"
- "Check-in is not possible at the moment"
+ "ሕጂ Check-in ዝከኣል ኣይኮነን"
- "Scan a SwissCovid QR code to check in on site."
+ "ሓደ ናይ SwissCovid QR ኮድ ስከን ግበሩ ኣብቲ ቦታ ምእንቲ ብቸክ-ኢን ክትኣትው።"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "ናይ ስዊዝ ኮቪድ ኮድ QR ኣብቲ ቦታ ንክትኣትዉ ስከን እንተገበርክኩም፣ ኣብዚ ኣውቶማቲካሊ መዝገብ ክዕቀብ እዩ።"
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "ኣቲኹም ዲኹም።"
+ "{DATE1} ክሳብ {DATE2}"
+ "ዝያዳ ብዛዕባ ኮድ ኮቪድ"
- "Remove entry"
+ "መዝገብ ምድምሳስ"
- "Delete"
+ "ምድምሳስ"
- "Share"
+ "ምክፍፋል"
- "Print"
+ "ፕሪንት ምግባር"
- "Scan a SwissCovid QR code to check in on site."
+ "ሓደ ናይ SwissCovid QR ኮድ ስከን ግበሩ ኣብቲ ቦታ ምእንቲ ብቸክ-ኢን ክትኣትው።"
- "Share check-in entries"
+ "ቸክ-ኢን መዝገበኣት ምክፍፋል"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "ናይ ቸክ-ኢን መዝገባትኩም እንድሕር ኣካፊልኩም፣ እቲ ኤፕ ኣብ ማዕረ ግዜ ኣብ ሽዑ ቦታ ኔሮም ዝነበሩ ሰባት ክሕብሮም ይኽእል።"
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "ኩሉ ምምራጽ"
+ "ኣይትስደድ"
+ "ዘይምክፍፋል"
+ "ናይ ብሓቂ ብዘይ ምክፍፋል፧"
+ "ምክፍፋል ግላውያን መፋትሕኩም ን«SwissCovid» ይሕግዝ፣ ኣብ ምሕባር ናብ ካልእ ተለቢዶም ዝኾኑ ሰባት ከምኡ ድማ ቀጻሊ ምዝርጋሕ እዚ ቫይረስ ንክውገድ። ምክፍፋል ግላውያን መፋትሕኩም ዘይትገብሩ እንተኾይንኩም፣ ተለቢዶም ዝኾኑ ሰባት ኣይክሕበሩን እዮም።"
- "Remove entry"
+ "መዝገብ ምድምሳስ"
- "Your entry will be permanently removed from the list."
+ "መዝገብኩም ሓንሳዕ ንሓዋሩ ካብቲ ዝርዝር ክድምሰስ እዩ።"
- "Completely remove"
+ "ብምልኡ ምልጋስ"
- "Only hide and keep receiving notifications?"
+ "ጥራይ ምሕባእ ከምኡ‘ውን ንቀጻሊ ክሕበር ይግባእ፧"
- "Tested positive?"
+ "ፖዚቲቭች ተመርሚርካ/ኪ፧"
- "Reactivate encounters and check-in"
+ "ምርኽኻብን ቸክ-ኢንን እንደገና ምውላዕ።""{HOURS} h {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "ፖዚቲቭ ቸክ-ኢን እንድሕር ጌርኩም፣ ክትኣትዉ ኣይትኽእሉን ኢኹም።"
- "Generate a QR code that people can use to check in on site."
+ "ኮድ QR ምስፈጠርካዮ ብእኡ ሰባት ኣብቲ ቦታ ክኣትዉ ይኽእሉ።"
- "Are you sure you want to delete the QR code?"
+ "ነቲ ኮድ QR ናይብሓቂ ክትድምሥዎ ደሊኹም ዶ፧"
- "Self check-in"
+ "ባዕልኻ እትገብር ቸክ-ኢን"
- "Scan a SwissCovid QR code to check in on site."
+ "ሓደ ናይ SwissCovid QR ኮድ ስከን ግበሩ ኣብቲ ቦታ ምእንቲ ብቸክ-ኢን ክትኣትው።"
- "Saving not possible"
+ "ምዕቃብ ዝከኣል ኣይኮነን"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "ዝመረጽኩሞም ግዜታት ምስቶም ካልኦት ካብቲ መዝገበ-ዕለትኩም ዝመጽኡ ቸክ-ኢናት ኣይሰማምዑን እዮም። ሓበሬታኹም እንደገና ኣቈጻጸሩ ብኽበርትኩም።"
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "ዝመረጽኩሞም ቸክ-ኢን ተሰዲዶም ኔሮም። ኣብ ማዕረ ግዜ ኣብ ሽዑ ቦታ ኣትዮም ዝነበሩ ሰባት ክሕበሩ እዮም።"
- "SwissCovid check-in"
+ "SwissCovid-Check-in"
- "Check in without leaving any data"
+ "ምእታው ብዘይ ምግዳፍ ሓበሬታ/ዳታ"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "ምስቲ ናይ SwissCovid ኤፕ ኣባና ብቐሊል ብቸክ-ኢን ክትኣትዉ ትኽእሉ ሓበሬታይ ብዘይምግዳፍ። ኩነተ ርከሳ እንተሎ በቲ ኤፕ ቀልጢፍኩም ክትሕበሩ ኢኹም።"
- "SwissCovid app"
+ "ኤፕ SwissCovid"
- "Download now"
+ "ሕጂ ምምጻእ ብዳውንሎድ"
- "Check in without leaving
-data"
+ "ቸክ-ኢን ብዘይ ሓበሬታ
+ምግዳፍ"
- "Enter your Covidcode here to alert others."
+ "ኣብዚ ኮቪድ ኮድኩም ኣእትዉ ንካልኦት መታን ከተጠንቅቕዎም።"
- "Isolation ended?"
+ "ግላለ ምዝዛም፧"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "ቸክ-ኢን ከተቕርቡ ደሊኹም ዶ፧"
+ "ስለምንታይ ናይ ቸክ-ኢን ኣገባብ፧"
+ "ናይ ቸክ-ኢን ኣገባብ ብዘይ ምሓዝ ሓበሬታ ርክብ ነቲ SwissCovid ኤፕ ይውስኾ። ምስዚ ኣገባብ‘ዚ ፖዚቲቭ እንተ ደኣ ተመርሚርኩም፣ ጥራይ ቀረባ ርክባት ዘይኮነ ዋላ ውን ኣብ ማዕረ ግዜን ማዕረ ቦታን ኣትዮም ዝነበርይ ሰባት ከተጠንቅቕዎም ትኽእሉ።"
+ "ማእከላዊ ዕቃበ ዳታ የለን"
+ "ነቲ ናይ SwissCovid-ቸክ-ኢን ግላዊ ዳታ ከም ስም፣ ኣድራሻ ወይ ቁጽሪ ተሌፎን ክወሃብ ኣየድልን ከምኡ‘ውን ናይ GPS ሓበሬታ ቦታ ውን ኣይክተሓዝን እዩ። ዝርዝር ቸክ-ኢን ብዘይ ኢንተርነት ኣብ ሞባይልኩም ክዕቀብ እዩ። ድሕሪ 14 መዓልትታት እቶም ኣብ ሞባይልኩም ዘሎ ሓበሬታ ክድምሰስ እዩ።"
+ "ኮድ QR ፈጥሩ"
+ "ምስ ሓደ SwissCovid-QR-ኮድ ሰባት ኣብቲ ቦታ ሓበሬታኦም ብዘይምግዳፍ ቸክ-ኢን ክገብሩ ይኽእሉ።"
+ "ነቲ ግድነታዊ ምልዓል ዳታ ርክብ መተካእታ የለን"
+ "ቸክ-ኢን SwissCovid መተካእታ ንኣብ «ኮቪድ-19-መትከል ፍሉይ ኩነት» ዝተኣዘዘ ግዴታ ምልዓል ዳታ ርክብ ኣይኮነን።"
+ "እዚኦም Check-ins ኣበይ ክጥቀሙ ይከኣሉ፧"
+ "ስለምንታይ ናይ ቸክ-ኢን ኣገባብ፧"
+ "ኣብ ሰብ ዝራኸቡ ቦታ እቲ ስዊዝ ቸክ-ኢን ውን ክጥቐም ይኽእል፣ ግና ግዴታ ናይ ምልዓል ዳታ ርክብ የለን።
+
+ዝኾኑ ቦታታት ስራሕ፤
+– ግላውያን ፍጻሜታት
+– ፍጻሜታት ማሕበር
+– ኣኼባታት ኣብ ሞያዊ ከባቢ ከም ንኣ. ክፍሊ ዋዕላ፣ ክፍሊ ቤትጽሕፈት፣ ካንቲናታት፣ ወዘተ"
+ "ናይ ቸክ-ኢን ኣገባብ ብዘይ ምሓዝ ሓበሬታ ርክብ ነቲ SwissCovid ኤፕ ይውስኾ። ምስዚ ኣገባብ‘ዚ ፖዚቲቭ እንተ ደኣ ተመርሚርኩም፣ ጥራይ ቀረባ ርክባት ዘይኮነ ዋላ ውን ኣብ ማዕረ ግዜን ማዕረ ቦታን ኣትዮም ዝነበርይ ሰባት ከተጠንቅቕዎም ትኽእሉ።"
- "Risk situation"
+ "ኩነት ርከሳ"
- "Encounters"
+ "ምርኽኻብ"
- "Check-in"
+ "ቸክ-ኢን"
- "What should I do?"
+ "እንታይ ክገብር ኣለኒ፧"
- "Positive check-in"
+ "ኣወንታዊ Check-in"
- "Positive check-ins/contacts"
+ "ኣወንታዊ Check-ins / ርክባት"
- "User QR code"
+ "User QR Code"
- "Contact tracing QR code"
+ "Contact Tracing QR Code"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "ድሕሪ ሓደ ኣወንታዊ ውጽኢት መርመራ ናይ ቸክ-ኢን ኣገባብ ኣውቶማቲካሊ ተዓጽዩ እዩ። ድሕሪ ምድጻም ግላለኹም እንደገና ክውላዕ ይከኣል።"
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "እቲ ቦታ ወይ ፍጻሜኹም ብቀረባ ዝገልጽ ኣርእስቲ ወይ ስም ሃቡ ኢኹም።
+እዚ ኣርእስቲ ኣብ Check-in ክረአ እዩ።"
+ "እንታይ ክትገብሩ ትኽእሉ፧"
+ "ርክባት ምክልኻል"
+ "ምልክታት ምጥንቃቕ"
+ "ምምርማር"
+ "ድሮ ተላጋቢ ክትኮኑ ትኽእሉ ከይተገንዘብኩም። እቶም ብቑዓት ናይ ጽሬትን ምክልኻልን ስጉምትታት ተኸተሉ ከምኡውን ስድራኹምን ኣዕሩኽትኹምን ከባብኹምን ኣስተውዕሉ።"
+ "መርገጽ ጥዕናኹም ተኸታተሉ።"
+ "ኣብ ምቕልቓል ምልክታት ብዝቐልጠፈ ተመርመሩ። ዋላ ምልክታት ዘይቕልቀሉ እንተኾይኖም፣ መርመራ ኮሮና ጤቓምን ዝምረጽን እዩ።"
- "Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "ቸክ-ኢን"
+ "ዝኾነ ዳታ ኣይተሰደደን"
+ "ዝኾነ ግላዊ መፍትሕ ኣይተሰደደን።"
+ "ተመለስ"
+ "ዝኾነ ዳታ ዘይምስዳድ"
+ "ቸክ-ኢን መዝገባት ኣይተሰደዱን።"
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
+ "ምክፍፋል መርጫ"
+ "ዋላሓደ ዘይምክፋል"
+ "ምእታው ብዘይ ምግዳፍ ሓበሬታ/ዳታ""Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "ምስቲ SwissCovid-ቸክ-ኢን ምስቲ ኤፕ ኣብ ሓደ ቦታ ወይ ኣብ ሓደ ምርኽኻብ ክትኣትዉ ትኽእሉ ኢኹም።"
+ "ህላወኹም ጥራይ ኣብቲ ኤፕ እዩ ዝዕቀብ። ዝኾነ ግላዊ ሓበሬታ ኣብቲ ኤፕ ክትገድፉ ኣየድልየኩምን።"
+ "ምስቲ SwissCovid-ቸክ-ኢን ምስቲ ኤፕ ኣብ ሓደ ቦታ ወይ ኣብ ሓደ ምርኽኻብ ክትኣትዉ ትኽእሉ ኢኹም። ህላወኹም ጥራይ ኣብቲ ኤፕ እዩ ዝዕቀብ። ዝኾነ ግላዊ ሓበሬታ ኣብቲ ኤፕ ክትገድፉ ኣየድልየኩምን። ሽዑ እቶም ናይ ኣጠቓቕማ ኩነታት ከምኡ'ውን ስምምዕ ምክልኻል ዳታ ክሕደሱ እዮም።"
+ "ገዲፍካ ምቕጻል"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "እቲ መዝገብ ን14 መዓልታት ዝተመስጠረ ኣብቲ ዳሕረዋይ ኤፕ ክዕቀብ እዩ። ኣብ ክኸውን ዝኽእል ልበዳ ብዝኾነ ክትሕበሩ ትኽእሉ ኢኹም።"
- "Hide only"
- "The app can remind you to check out at the right time."
+ "ጥራይ ምሕባእ"
+ "እቲ ኤፕ ንልክዕ ግዜ ክትወጽኡ ከዘክረኩም ይኽእል።"
- "Or remove completely?"
+ "ወይ ብምልኡ ምድምሳስ፧"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "እቲ መዝገብ ብምልኡ ክድምሰስ እዩ። ክትጥንቀቑ ኣይትኽእሉን ኢኹም።"
+ "ማንም ሰብ ኣይክሕበርን እዩ።"
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "ብዛዕባ ምምርማር እንታይ ክፈልጥ ኣለኒ፧"
+ "ኤፕ ምትካልን ምእታውን"
+ "እቲ ምስትኽኻል ኤፕ ወድእዎ፣ መታን ኣገባብ ምክትታል ክትጥቐመሉ።"
+ "ሕጂ ምውዳእ"
+ "ምስትኽኻል ምውዳእ"
+ "ምዝርጋሕ ኮሮናቫይረስ ንምድራት እትገብሩ ምትሕብባር ነመስግነኩም ኢና።"
+ "ኣብ ርከሳ ልበዳ ብቕልጡፍ ተሓቢርኩም።""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 bb6dff3e3..52a2456e7 100644
--- a/common/src/main/res/values-tr/strings.xml
+++ b/common/src/main/res/values-tr/strings.xml
@@ -101,7 +101,7 @@
"Olası enfeksiyon"
- "Enfeksiyon olasılığı mevcuttur."
+ "Hastalık size bulaşmış olabilir""Uygulama etkindir"
@@ -191,7 +191,7 @@
"Kovid kodu ile..."
- "Enfeksiyon zincirini kır"
+ "Olası bulaşmalara karşı uyar""Kovid kodu gir"
@@ -224,8 +224,8 @@
"Semptomlar""Takip devre dışı bırakıldı""Uygulamanın çalışabilmesi için takibi etkinleştirin."
- "Bluetooth'u kapat"
- "Takibin çalışabilmesi için cep telefonunuzun Bluetooth özelliğini etkinleştirin."
+ "Bluetooth kapalı"
+ "Anonim takibin çalışabilmesi için cep telefonunuzun Bluetooth özelliğini etkinleştirin.""Bluetooth'u ektinleştirin""Bluetooth kullanımı rededildi""Takibin çalışabilmesi için Bluetooth kullanımına izin verin."
@@ -257,11 +257,11 @@
"İzolasyona ilişkin size bildirilen talimatlara uyun"
- "Takip sonlandırıldı"
- "Temaslarınız artık kaydedilmiyor."
+ "Temaslar ve Check-in devre dışı"
+ "Temaslar kaydedilmiyor""... ile iletişime girin""...'lere/lara uyun"
- "Kendini İzole Etme Talimatları"
+ "İzolasyon Talimatları""Yeni bildirim"
@@ -271,7 +271,7 @@
"Testiniz pozitif"
- "Enfeksiyon riski bulunmaktadır."
+ "Hastalık size bulaşmış olabilir""Korona virüsü testiniz pozitiftir."
@@ -325,7 +325,7 @@
"SwissCovid uygulaması ile Korona virüsünün yayılımını sınırlandırmaya yardımcı olabilirsiniz."
- "Korona virüsü ile potansiyel temaslarda uygulama sizi bilgilendirir."
+ "Korona virüsü ile potansiyel temaslarda uygulama sizi uyarır.""Privacy by Design"
@@ -340,7 +340,7 @@
"Rastgele ID'ler telefonunuza kaydedilir ve 14 gün sonra silinir."
- "Uygulama nasıl çalışır?"
+ "Temaslar""Bluetooth sayesinde temasların tespit edilmesi"
@@ -358,7 +358,7 @@
"Enfeksiyon riskinde dair bildirim"
- "Uygulama, potansiyel olarak Korona virüsü ile temasınız olduğunda sizi uyarır."
+ "Korona virüsü ile potansiyel temaslarda uygulama sizi uyarır.""Doğru davranışlarla enfeksiyon zincirlerini kırabilir ve diğer insanları koruyabilirsiniz."
@@ -391,7 +391,7 @@
"Mesajlara izin ver"
- "Uygulamanın sizi bilgilendirebilmesi için mesajlara izin vermeniz gerekmektedir."
+ "Uygulamanın sizi uyarabilmesi için bildirimlere izin vermeniz gerekir.""Mesajlara izin ver"
@@ -412,7 +412,7 @@
"Uygulama kuruldu"
- "Enfeksiyon zincirlerini kırmaya yardımcı olduğunuz için teşekkür ederiz."
+ "Korona virüsün yayılmasını önlemeye yardımcı olduğunuz için teşekkür ederiz.""Başlat"
@@ -439,7 +439,7 @@
"Yeni şüphe"
- "Bir enfeksiyon şüphesi vardır."
+ "Hastalık size bulaşmış olabilir""Saat hatası"
@@ -573,8 +573,9 @@ Bunlar cep telefonunda kaydedilmekte ve 14 gün sonra silinmektedir.""Bildirimler nedir?"
- "Uygulama düzenli olarak temas ettiğiniz kişilerin pozitif bir test sonucu bildirip bildirmediğini kontrol eder.
-Olası bir enfeksiyon riskinde kilit ekranında bir mesaj ile bilgilendirileceksiniz."
+ "Uygulama düzenli olarak, temas ettiğiniz veya sizinle aynı yerde bulunan diğer kullanıcıların pozitif bir test sonucu bildirip bildirmediğini kontrol eder.
+
+Olası bir bulaşma durumunda kilit ekranındaki gösterilecek bir bildirim ile bilgilendirilirsiniz.""SwissCovid Bilgi Hattı sizi ücretsiz testler konusunda bilgilendirir."
@@ -610,8 +611,9 @@ Böylece uygulama üzerinden yalnızca onaylı vakaların bildirilmesi sağlanı
"Neler iletilir?"
- "Şahsi bilgileriniz değil, uygulamanızın yalnızca özel kodları iletilir.
-Bu şekilde diğer SwissCovid uygulaması kullanıcıları yalnızca bir bulaşma riski olabileceğini öğenir."
+ "Şahsi verileriniz değil, sadece uygulamanızın özel kodları ve yaptığınız Check-in’lerin şifrelenmiş ID’leri iletilir.
+
+ Bu şekilde diğer SwissCovid uygulaması kullanıcıları bir bulaşma riski söz konusu olabileceğini öğrenir.""+41 58 387 77 78"
@@ -620,10 +622,10 @@ Bu şekilde diğer SwissCovid uygulaması kullanıcıları yalnızca bir bulaşm
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/faq-kontakte-downloads/haeufig-gestellte-fragen.html?faq-url=/de/categories/swisscovid-app"
- "Önemli Bilgiler"
+ "Girişler Hakkında Bilgiler"
- "Kişisel bilgileriniz iletilmese dahi, bazı kişiler tarihler temelinde sizinle olan teması hatırlayabilirler."
+ "Kişisel bilgileriniz iletilmezse dahi, bir kişini tarihi dikkate alarak sizinle temas ettiğini hatırlayabilir.""Kabul ediyorum"
@@ -654,7 +656,7 @@ Ayrıca bu semptomlar görülebilir:
"İzleme neden devre dışı bırakıldı?"
- "Pozitif bir testten sonra uygulamadaki izleme otomatik olarak devre dışı bırakılır. İzolasyonu sona erdiğirdiğiniz anda, izleme yeniden etkinleştirilebilir."
+ "Pozitif bir test sonucundan sonra uygulamadaki takip otomatik olarak devre dışı bırakılır. İzolasyonu sona erdirdiğiniz andan itibaren, takip fonksiyonu yeniden etkinleştirilebilir.""tr"
@@ -694,7 +696,7 @@ Ayrıca bu semptomlar görülebilir:
"Takibi etkinleştir"
- "Uygulamayı kullanabilmek için KOVİD-19 temas bildirimlerini etkinleştirmiş olmalısınız."
+ "Temasların algılanabilmesi için takip fonksiyonunun etkinleştirilmiş olması gerekmektedir.""Etkinleştir"
@@ -723,10 +725,10 @@ Ayrıca bu semptomlar görülebilir:
"İzolasyonu sona erdir"
-
+
"Bildirimi gerçekten silmek mi istiyorsunuz?""Pil optimizasyonunu yoksay"
- "Temasların her zaman takip edebilebilmesi için pil optimizasyonunun bu uygulama için devre dışı bırakılması gerekmektedir."
+ "Uygulamanın düzgün çalışabilmesi için pil optimizasyonunun bu uygulama için devre dışı bırakılmış olması gerekir.""İzin ver""OK""Yalnızca bu uygulama için"
@@ -748,7 +750,7 @@ Ayrıca bu semptomlar görülebilir:
"KOVİD-19 temas mesajları devre dışı bırakıldı"
- "Takibin çalışabilmesi için KOVİD-19 iletişim mesajlarının etkinleştirilmesi gerekmektedir."
+ "Anonim takibin çalışabilmesi için {TRACING_SETTING_TEXT}’i etkinleştirin.""Mesaj iletilemedi. Lütfen rastgele ID'lerinizin SwissCovid uygulaması ile paylaşılmasına izin verin."
@@ -790,8 +792,9 @@ Ayrıca bu semptomlar görülebilir:
"SwissCovid uygulama kapatıldığında da aktiftir."
- "SwissCovid enfeksiyon zincirlerini kırar."
- "Kişisel bilgileriniz iletilmese dahi, bazı kişiler tarihler temelinde sizinle olan teması hatırlayabilirler."
+ "SwissCovid –
+bir bulaş riski durumunda hızlıca bilgi sahibi olun."
+ "Evet, ancak kişisel bilgileriniz iletilmezse dahi, bazı kişiler tarihler temelinde sizinle olan teması hatırlayabilirler.""Bilgilerim anonim mi?"
@@ -801,7 +804,7 @@ Ayrıca bu semptomlar görülebilir:
"KOVİD-19 Bildirimleri"
- "Uygulamanın kullanılabilmesi için KOVİD-19 bildirimlerinin etkinleştirilmiş olması gerekmektedir."
+ "Temasların algılanabilmesi için takip fonksiyonunun etkinleştirilmiş olması gerekmektedir.""SwissCovid"
@@ -894,7 +897,7 @@ Schwarzenburgstrasse 157
"Seyahatler"
- "SwissCovid şu ülkelerde çalışır:"
+ "SwissCovid temas takibi şu ülkelerde çalışır:""Bunun anlamı nedir?"
@@ -924,12 +927,6 @@ Yurt dışı seyahatlerinden gelen kişilerin de uyarılabilmesi için bu, sizin
"+41 58 387 77 80"
-
-
-
-
-
-
"Maruz Kalma Bildirimleri""SwissCovid"
@@ -1211,9 +1208,9 @@ Yurt dışı seyahatlerinden gelen kişilerin de uyarılabilmesi için bu, sizin
"Sona erdir"
- "Temaslar artık kaydedilmeyecek.
+ "Artık temaslar kaydedilmiyor ve yeni Check-in’ler yapmak de artık mümkün değil.
-İzolasyondan sonra izleme yeniden etkinleştirilebilir."
+ Her iki fonksiyon da İzolasyon sona erdikten sonra yeniden etkinleştirilebilir.""https://www.ag.ch/de/themen_1/coronavirus_2/coronavirus.jsp""https://www.ar.ch/verwaltung/departement-gesundheit-und-soziales/amt-fuer-gesundheit/informationsseite-coronavirus/""https://www.ai.ch/themen/gesundheit-alter-und-soziales/gesundheitsfoerderung-und-praevention/uebertragbare-krankheiten/coronavirus"
@@ -1245,9 +1242,9 @@ Yurt dışı seyahatlerinden gelen kişilerin de uyarılabilmesi için bu, sizin
"https://www.llv.li/inhalt/118724/amtsstellen/coronavirus"
- "İşitmiyor veya işitme özürlü müsünüz ve Koronavirüs bilgi hattını arayamıyor musunuz?
+ "İşitme sorununuz var veya işitme engellisiniz ve telefon edemiyor musunuz?
-O halde covid-support@medgate.ch adresine bir e-posta gönderiniz"
+ O halde covid-support@medgate.ch adresine bir e-posta gönderin.""Covid kodları"
@@ -1301,7 +1298,7 @@ Zaman, Koronavirüs ile mücadelede en önemli faktördür: Semptom başlangıc
"Testiniz pozitif çıktı (PCR testi veya hızlı antijen testi) ve 4 saat sonra halen bir Covid kodu almadınız mı?
-O halde koronavirüz danışma hattıyla iletişim kurun:"
+O halde şu numarayı arayın:"
"Covid kodunu henüz almadınız mı?"
@@ -1325,7 +1322,7 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"Uyumlu ülkeler:"
- "Yurt dışı seyahatlerinden dönen kişilere de uyarıda bulunulabilmesi için, özel kodlar uyumlu ülkelerin Korona uygulamalarıyla paylaşılır."
+ "Yurt dışından gelen ziyaretçilerin de bir temastan sonra uyarılabilmesi için kişiye özel kodlar uygulamaları uyumlu olan ülkelerin korona uygulamalarıyla paylaşılır.""Toplam"
@@ -1340,13 +1337,13 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"Kimlere haber verilir?"
- "Mesafe ve süre nedeniyle bir bulaşma olasılığı mevcut ise {ONSET_DATE} gününden başlamak üzere, Covid kodunuzun girilmesine kadar, bulaşma döneminizin aralığındaki tüm temaslılara haber verilir."
+ "Mesafe ve süre nedeniyle veya aynı yere Check-in yapmış olmanız nedeniyle bir bulaşma riski söz konusu olduğunda {ONSET_DATE} tarihinden başlamak üzere, Covid kodunuzun girilmesine kadar geçen sürede temas ettiğiniz herkes durum hakkında bilgilendirilir.""{ONSET_DATE} – bugün"
- "Bu sayede enfeksiyon zincirlerinin durdurulması için yardımcı olacaksınız."
+ "Bu şekilde diğer kişilerin olası bir bulaşmaya karşı uyarılmasına yardım etmiş olursunuz.""Uygulamanızın özel anahtarları şu zaman aralığı için gönderildi:"
@@ -1385,46 +1382,46 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"1 gün sonra"
- "Check-out reminder"
+ "Check-out hatırlatması"
- "Do you want to check out?"
+ "Çıkış yapmak mı istiyorsunuz?"
- "Automatic check-out"
+ "Otomatik Check-out"
- "You were checked out automatically."
+ "Çıkış işleminiz otomatik olarak yapıldı."
- "Scan QR code and check in"
+ "Kare kodu okutun ve giriş yapın"
- "No valid QR code!"
+ "Geçerli kare kod yok!"
- "From"
+ "Başlangıç tarihi"
- "To"
+ "Bitiş tarihi"
- "Add note"
+ "Not ekle"
- "e.g. who were you with?"
+ "Örn. kiminle oradaydınız?"
- "Done"
+ "Bitti"
- "Check-out reminder"
+ "Check-out hatırlatması"
- "Off"
+ "Kapalı"
- "Authentication needed to access the diary"
+ "Günlüğe erişim için kendinizi yetkilendirin""{HOURS} h"
@@ -1433,25 +1430,25 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"{MINUTES}'"
- "The app needs to access the camera to be able to scan the QR code."
+ "Uygulamanın kare kodu tarayabilmek için kamera erişimine ihtiyacı var."
- "My check-ins"
+ "Check-in’lerim""Check-in"
- "The camera is needed to scan the QR code."
+ "Kare kodu taramak için kameraya ihtiyaç duyuluyor."
- "Allow Face ID to securely access your diary."
+ "Günlüğünüze güvenli bir şekilde erişebilmek için Face-ID’ye izin verin."
- "Authentication needed to access the diary"
+ "Günlüğe erişim için kendinizi yetkilendirin"
- "Check-out"
+ "Check out""Check-in"
@@ -1460,288 +1457,286 @@ O halde koronavirüz danışma hattıyla iletişim kurun:"
"Check-in"
- "Generate QR code"
+ "Kare kod oluşturun"
- "Offer check-ins?"
+ "Check-in’leri öner?"
- "Check in now"
+ "Şimdi check-in yap"
- "The reminder could not be set"
+ "Hatırlatma oluşturulamadı"
- "To receive check-out reminders, you need to allow \\"notifications\\" in the app settings."
+ "Check-out hatırlatmasını alabilmeniz için uygulama ayarlarında \”Bildirimler\”e izin verin."
- "Customise"
+ "Uyarla"
- "Edit"
+ "Düzenle"
- "No entries available yet"
+ "Henüz giriş yok"
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Bir yere check-in yapmak için bir SwissCovid kare kodunu taradığınızda buraya otomatik olarak bir kayıt oluşturulur."
- "SwissCovid needs to access your device's camera. The camera is only used to scan QR codes."
+ "SwissCovid’in cihazınızın kamerasına erişimine sahip olması gerekiyor. Kamera sadece kare kodları taramak için kullanılacaktır."
- "Allow access to camera"
+ "Kamera erişimine izin ver"
- "e.g. room or event name"
+ "Örn. Oda veya etkinlik adı"
- "Print PDF"
- "Info"
- "Network error"
- "Check your internet connection."
- "Try again"
- "Notifications deactivated"
- "Modify your settings to be informed straight away if you receive a notification."
- "Modify settings"
- "No access to camera"
- "The app needs to access the camera to be able to scan the QR code."
- "Error"
- "QR code not yet valid"
- "QR code no longer valid"
- "You are already checked in."
- "This QR code is not compatible with the installed version of the app. Please update the app to continue."
- "Update required"
- "Update now"
+ "PDF yazdır"
+ "Bilgi"
+ "Ağ hatası"
+ "İnternet bağlantısını kontrol edin."
+ "Yeniden dene"
+ "Bildirimler devre dışı"
+ "Bir bildirim ile doğrudan bilgi alabilmek için ayarları değiştirin."
+ "Ayarları değiştir"
+ "Kamera erişimi yok"
+ "Uygulamanın kare kodu tarayabilmek için kamera erişimine ihtiyacı var."
+ "Hata"
+ "Kare kod henüz geçerli değil"
+ "Kare kod artık geçerli değil"
+ "Zaten check-in yaptı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""Check out"
- "Snooze"
- "Permanent check-in notification"
- "You have been checked in since {TIME}."
+ "Sustur"
+ "Kalıcı Check-in Bildirimi"
+ "Saat {TIME}’den beri check-in yapmış durumdasınız."
- "Save"
+ "Emniyete al"
- "Scan QR code"
+ "Kare kod tara"
- "Check-in deactivated"
+ "Check-in devre dışı"
- "Check-in is not possible at the moment"
+ "Şu anda check-in yapılamıyor"
- "Scan a SwissCovid QR code to check in on site."
+ "Bulunduğunuz yere check-in yapmak için bir SwissCovid kare kodunu tarayın."
- "If you scan a SwissCovid QR code to check in on site, an entry will automatically be created here."
+ "Bir yere check-in yapmak için bir Swiss-Covid kare kodunu taradığınızda buraya otomatik olarak bir kayıt oluşturulur."
- "You're checked in"
- "{DATE1} to {DATE2}"
- "More about the Covidcode"
+ "Check-in yaptınız"
+ "{DATE1} ile {DATE2} arasında"
+ "Covid kodu hakkında daha fazla bilgi"
- "Remove entry"
+ "Kaydı sil"
- "Delete"
+ "Sil"
- "Share"
+ "Paylaş"
- "Print"
+ "Yazdır"
- "Scan a SwissCovid QR code to check in on site."
+ "Bulunduğunuz yere check-in yapmak için bir SwissCovid kare kodunu tarayın."
- "Share check-in entries"
+ "Check-in kayıtlarını paylaş"
- "If you share check-in entries, the app can notify people who were checked in at the same location at the same time."
+ "Check-in kayıtlarınızı paylaştığınızda, uygulama aynı zaman aralığında aynı yerde check-in yapmış olan kişileri bu durum hakkında bilgilendirebilir."
- "Select all"
- "Don't send"
- "Don't share"
- "Are you sure you don't want to share?"
- "Sharing your private key helps SwissCovid to notify other people who may be infected and to stop the virus spreading. If you don't share your private key, people who may be infected will not be notified."
+ "Tümünü seç"
+ "Gönderme"
+ "Paylaşma"
+ "Gerçekten paylaşılmasın mı?"
+ "Özel kodlarınızın paylaşılması «SwissCovid»’e, hastalığın bulaşmış olabileceği kişileri durumdan haberdar etmesi ve böylece virüsün daha da fazla yayılmasının önlenmesine yardımcı olur. Özel kodlarınızı paylaşmadığınızda hastalığın bulaşmış olması potansiyeli olan kişiler bu durumdan haberdar edilmez."
- "Remove entry"
+ "Kaydı sil"
- "Your entry will be permanently removed from the list."
+ "Kaydınız kalıcı olarak listeden silinecektir."
- "Completely remove"
+ "Kalıcı olarak sil"
- "Only hide and keep receiving notifications?"
+ "Sadece gizleyip bildirimleri almaya devam et?"
- "Tested positive?"
+ "Testiniz pozitif mi sonuçlandı?"
- "Reactivate encounters and check-in"
+ "Temasları ve Check-in’i yeniden aktif et.""{HOURS} h {MINUTES}'"
- "You can no longer check in if you have tested positive."
+ "Test sonucunuz pozitif ise artık check-in yapamazsınız."
- "Generate a QR code that people can use to check in on site."
+ "Kişilerin ilgili yere check-in yapabileceği bir kare kod oluşturun."
- "Are you sure you want to delete the QR code?"
+ "Kare kodu gerçekten silmek istiyor musunuz?"
- "Self check-in"
+ "Kendi kendine check-in"
- "Scan a SwissCovid QR code to check in on site."
+ "Bulunduğunuz yere check-in yapmak için bir SwissCovid kare kodunu tarayın."
- "Saving not possible"
+ "Emniyete alınamıyor"
- "The times you selected clash with other check-ins in your diary. Please check your entry."
+ "Seçmiş olduğunuz zaman aralıkları günlüğünüzde bulunan diğer check-in’ler ile kesişiyor. Lütfen bilgileri bir kez daha kontrol edin."
- "The check-in entries you selected have been sent. Everyone who was checked in at the same location at the same time will be notified."
+ "Seçmiş olduğunuz check-in kayıtlarınız gönderildi. Aynı zaman aralığında aynı yere check-in yapmış olan tüm kişilere bilgi verilecek."
- "SwissCovid check-in"
+ "SwissCovid-Check-in"
- "Check in without leaving any data"
+ "Kayıtlı veri bırakmadan check-in yap"
- "The SwissCovid app lets you simply check in with us without leaving behind your data. If there is a risk of infection, you will be rapidly notified in the app."
+ "SwissCovid uygulaması ile kayıtlı veri bırakmadan kolayca check-in yapabilirsiniz. Bir risk durumunda uygulama üzerinden hızlıca bilgilendirilirsiniz."
- "SwissCovid app"
+ "SwissCovid Uygulaması"
- "Download now"
+ "Şimdi indir"
- "Check in without leaving
-data"
+ "Kayıtlı veri
+bırakmadan check-in yap"
- "Enter your Covidcode here to alert others."
+ "Başkalarını uyarmak için covid kodunuzu buraya girin."
- "Isolation ended?"
+ "İzolasyonu sonlandır?"
- "Do you want to offer check-ins?"
- "Why a check-in function?"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
- "No central data storage"
- "For the SwissCovid check-ins, you don't have to enter personal details, such as name, address or phone number, and no GPS location data is recorded. The list of check-ins is stored locally on your device. The local data is deleted after 14 days. "
- "Generate a QR code"
- "Using a SwissCovid QR code, people can check in on site without leaving their data."
- "No substitute for mandatory recording of contact details"
- "SwissCovid check-ins are no substitute for the obligation to record contact details provided for in the COVID-19 Ordinance Special Situation. "
- "Where can check-ins be used?"
- "Why a check-in function?"
- "SwissCovid check-ins can be used where people gather but where there is no obligation to record contact details.
-
-Possible locations:
-– Private events
-– Club meetings or events
-– Gatherings in professional settings, e.g. meeting rooms, offices, canteens"
- "The SwissCovid app has been upgraded to include the check-in function without entering contact details. If you test positive, this function lets you alert people who were checked in at the same location at the same time, as well as close contacts."
+ "Check-in’leri önermek mi istiyorsunuz?"
+ "Bir check-in uygulaması neden gerekli?"
+ "İletişim verileri olmadan çalışan check-in fonksiyonu SwissCovid uygulamasının kapsamını genişletiyor. Bu fonksiyon ile testinizin pozitif sonuçlanması halinde sadece yakın temasta bulunduğunuz kişileri değil, aynı zaman aralığında aynı yerde check-in yapmış olan kişileri de uyarabilirsiniz."
+ "Merkezi veri kaydı yok"
+ "SwissCovid-Check-in’leri için isim, adres veya telefon numarası gibi kişisel verilerin belirtilmesi gerekmez ve GPS konum bilgileri de kayıt edilmez. Check-in’lerin listesi yerel olarak cep telefonunuza kayıt edilir. 14 gün sonra bu yerel veriler yeniden cihazınızdan silinir."
+ "Bir kare kod oluşturun."
+ "Bir SwissCode kare kodu ile kişiler bir yere, geride kayıtlı herhangi bir veri bırakmadan check-in yapabilir."
+ "Zorunlu olan iletişim bilgileri kaydının ikamesi değildir"
+ "SwissCovid-Check-in’leri «Covid-19-Düzenlemesi Özel Durum» kapsamında talep edilen iletişim bilgileri kaydı yükümlülüğünü ikame etmez."
+ "Check-in’ler nerede kullanılabilir?"
+ "Bir check-in uygulaması neden gerekli?"
+ "SwissCovid-Check-in’leri insanların bir araya geldiği ancak iletişim bilgileri kaydı zorunluluğunun olmadığı yerlerde kullanılabilir.
+
+ Olası kullanım yerleri:
+– Özel etkinlikler
+– Dernek etkinlikleri
+– örneğin toplantı odaları, ofis alanları, yemekhaneler, vs. gibi bir araya gelinebilen profesyonel ortamlar."
+ "İletişim verileri olmadan çalışan check-in fonksiyonu SwissCovid uygulamasının kapsamını genişletiyor. Bu fonksiyon ile testinizin pozitif sonuçlanması halinde sadece yakın temasta bulunduğunuz kişileri değil, aynı zaman aralığında aynı yerde check-in yapmış olan kişileri de uyarabilirsiniz."
- "Risk situation"
+ "Risk durumu"
- "Encounters"
+ "Temaslar""Check-in"
- "What should I do?"
+ "Ne yapmalıyım?"
- "Positive check-in"
+ "Pozitif check-in"
- "Positive check-ins/contacts"
+ "Pozitif check-in’ler / temaslar"
- "User QR code"
+ "User QR Code"
- "Contact tracing QR code"
+ "Contact Tracing QR Code"
- "The check-in function is automatically deactivated after a positive test result. It can be reactivated after the end of isolation."
+ "Pozitif bir test sonucundan sonra check-in fonksiyonu otomatik olarak devre dışı bırakılır. İzolasyon sürenizin dolmasının ardından yeniden aktif edilebilir."
- "Enter a title that describes the location or your event in more detail. This title will be displayed on check-in."
- "What can you do?"
- "Protect your contacts"
- "Look out for symptoms"
- "Get tested"
- "You may already be infectious without realising it. Follow the rules on hygiene and social distancing and protect your loved ones and those around you."
- "Monitor your state of health."
- "If you develop symptoms, get tested immediately. Even if you don't have symptoms, a coronavirus test is advisable and recommended."
+ "Yeri veya etkinliği daha yakından tanımlayan bir başlık belirleyin.
+Bu başlık check-in sırasında gösterilir."
+ "Ne yapabilirsiniz?"
+ "Temaslıları koruyun"
+ "Semptomlara dikkat edin"
+ "Test yaptırın"
+ "Henüz kendiniz fark etmeden de hastalığı bulaştırabilirsiniz. Geçerli hijyen ve koruma önlemlerini dikkate alın ve ailenizi, arkadaşlarınızı ve çevrenizi koruyun."
+ "Sağlık durumunuzu gözetim altında tutun."
+ "Kendinizde semptomların varlığını gözlemlediğinizde hemen test yaptırın. Semptomlar olmadığında da bir korona testi mantıklıdır ve tavsiye edilmektedir.""Check-in"
-
-
-
- "No data sent"
- "No private key was sent."
- "Back"
- "Don't send data"
- "No check-in entries were sent."
+ "Gönderilen veri yok"
+ "Gönderilen özel kod yok."
+ "Geri"
+ "Veri gönderme"
+ "Check-in kayıtları gönderilmedi."
- "Share selection"
- "Don't share anything"
- "Check in without leaving data"
- "Check-ins"
- "With SwissCovid check-ins you can check in to a location or meeting using the app."
- "Your presence is only stored in the app. You don’t have to leave behind any personal data. "
- "With SwissCovid check-ins you can check in to a location or meeting using the app. Your presence is only stored in the app. You don't have to leave behind any personal data. The terms of use and privacy policy have been updated."
- "Skip"
+ "Seçimi paylaş"
+ "Hiçbir şey paylaşma"
+ "Kayıtlı veri bırakmadan check-in yap"
+ "Check-in’ler"
+ "SwissCovid-Check-in’leri ile uygulama ile bir yerde veya bir buluşmada check-in yapabilirsiniz."
+ "Orada bulunduğunuz sadece uygulama içinde kayıt edilir. Uygulama içinde kayıtlı kişisel veri bırakmanıza gerek yoktur."
+ "SwissCovid-Check-in’leri ile uygulama ile bir yerde veya bir buluşmada check-in yapabilirsiniz. Orada bulunduğunuz sadece uygulama içinde kayıt edilir. Uygulama içinde kayıtlı kişisel veri bırakmanıza gerek yoktur. Bunun için kullanım koşulları ve kişisel verilen korunması beyanı güncellenmiştir."
+ "Atla"
- "The entry is stored in encrypted form in the background of the app for 14 days. This means that you can still be notified if there is a possibility of infection."
+ "Kayıt 14 gün boyunca şifrelenmiş olarak uygulamanın arka planında saklanır. Olası bir bulaşma durumunda bu sayede yine de bilgilendirilebilirsiniz."
- "Hide only"
- "The app can remind you to check out at the right time."
+ "Sadece gizle"
+ "Uygulama size doğru zamanda check-out, yani çıkış yapmanızı hatırlatabilir."
- "Or remove completely?"
+ "Veya kalıcı olarak sil?"
- "The entry was removed completely. You can no longer be alerted."
- "Nobody will be notified."
+ "Kayıt kalıcı olarak silinir. Artık size uyarılar gönderilemez."
+ "Kimseye haber verilmez."
- "What do I need to know about testing?"
- "Install app and check in"
- "Complete the app configuration to be able to use the tracing function."
- "Complete now"
- "Configuration complete"
- "Thank you for helping stop the spread of coronavirus."
- "Find out fast if you're at risk of infection."
+ "Test yaptırmak hakkında bilmem gerekenler nelerdir?"
+ "Uygulamayı yükle ve check-in yap"
+ "Takip fonksiyonunu yeniden kullanabilmek için uygulama yapılandırmasını tamamlayın."
+ "Şimdi tamamla"
+ "Yapılandırma tamamlandı"
+ "Korona virüsün yayılmasını önlemeye yardımcı olduğunuz için teşekkür ederiz."
+ "Bir bulaş riski durumunda hızlıca bilgi sahibi olun.""https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ 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 a97154e9b..db87b7d88 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -740,8 +740,8 @@ Other symptoms may include:
"End isolation?"
-
- "Do you really want to delete the notification? "
+
+ "Do you really want to end the isolation?""Ignore battery optimisation""For the app to work, battery optimisation must be switched off.""Allow"
@@ -943,12 +943,6 @@ To enable travellers from abroad to also receive alerts, this happens regardless
"+41 58 387 77 80"
-
-
-
-
-
-
"Exposure Notifications""SwissCovid"
@@ -1740,9 +1734,6 @@ Possible locations:
"Check-in"
-
-
- "No data sent""No private key was sent.""Back"
@@ -1785,5 +1776,5 @@ Possible locations:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html"
-
+ "For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
\ No newline at end of file
From a6c5bf859eb635bf39f6456b581afdee9f1d2303 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 11:20:57 +0200
Subject: [PATCH 126/207] Remove all caps buttons
---
.../bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java | 2 +-
app/src/main/res/layout/card_checkin.xml | 3 +--
app/src/main/res/layout/card_what_to_do_checkin_report.xml | 1 -
app/src/main/res/layout/card_what_to_do_leitfaden.xml | 4 +---
app/src/main/res/layout/card_what_to_do_self_isolation.xml | 1 -
app/src/main/res/layout/fragment_checkin_overview.xml | 3 +--
app/src/main/res/layout/fragment_generate_qr_code.xml | 2 +-
app/src/main/res/layout/fragment_stats.xml | 1 -
app/src/main/res/layout/fragment_what_to_do_symptoms.xml | 3 +--
app/src/main/res/layout/item_events_explanation.xml | 2 +-
app/src/main/res/layout/item_generate_qr_code.xml | 2 +-
common/src/main/res/values/styles.xml | 5 -----
12 files changed, 8 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
index 39ca57805..7bfb3ff7b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -218,7 +218,7 @@ private void indicateInvalidQrCode(QRScannerState qrScannerState) {
invalidCodeText.setVisibility(View.INVISIBLE);
} else {
invalidCodeText.setVisibility(View.VISIBLE);
- color = R.color.tertiary;
+ color = R.color.status_red;
}
if (qrScannerState == QRScannerState.INVALID_FORMAT) {
diff --git a/app/src/main/res/layout/card_checkin.xml b/app/src/main/res/layout/card_checkin.xml
index 5a8850838..fbce02ff8 100644
--- a/app/src/main/res/layout/card_checkin.xml
+++ b/app/src/main/res/layout/card_checkin.xml
@@ -105,8 +105,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/spacing_medium_large"
- android:text="@string/checkout_button_title"
- android:textAllCaps="false" />
+ android:text="@string/checkout_button_title" />
diff --git a/app/src/main/res/layout/card_what_to_do_checkin_report.xml b/app/src/main/res/layout/card_what_to_do_checkin_report.xml
index ce890d778..31e417009 100644
--- a/app/src/main/res/layout/card_what_to_do_checkin_report.xml
+++ b/app/src/main/res/layout/card_what_to_do_checkin_report.xml
@@ -85,7 +85,6 @@
android:paddingTop="15dp"
android:paddingBottom="10dp"
android:text="@string/delete_reports_button"
- android:textAllCaps="true"
android:textColor="@color/blue_main" />
diff --git a/app/src/main/res/layout/card_what_to_do_leitfaden.xml b/app/src/main/res/layout/card_what_to_do_leitfaden.xml
index 2819461fa..a10e81a4f 100644
--- a/app/src/main/res/layout/card_what_to_do_leitfaden.xml
+++ b/app/src/main/res/layout/card_what_to_do_leitfaden.xml
@@ -112,8 +112,7 @@
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_launch"
android:paddingHorizontal="@dimen/spacing_medium"
- android:text="@string/meldungen_detail_open_leitfaden_button"
- android:textAllCaps="true" />
+ android:text="@string/meldungen_detail_open_leitfaden_button" />
+ android:text="@string/checkout_button_title" />
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 54f5d1484..387feac47 100644
--- a/app/src/main/res/layout/fragment_generate_qr_code.xml
+++ b/app/src/main/res/layout/fragment_generate_qr_code.xml
@@ -98,7 +98,7 @@
+ android:text="@string/symptom_detail_box_button" />
diff --git a/app/src/main/res/layout/item_events_explanation.xml b/app/src/main/res/layout/item_events_explanation.xml
index a38fa2b37..b270f50ef 100644
--- a/app/src/main/res/layout/item_events_explanation.xml
+++ b/app/src/main/res/layout/item_events_explanation.xml
@@ -98,7 +98,7 @@
diff --git a/common/src/main/res/values/styles.xml b/common/src/main/res/values/styles.xml
index 6d36b6355..674ae7b0f 100644
--- a/common/src/main/res/values/styles.xml
+++ b/common/src/main/res/values/styles.xml
@@ -236,10 +236,6 @@
false
-
-
@@ -261,7 +257,6 @@
From f5df1d0359f6de5944eb411e790bb0b486946aaa Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 12:01:15 +0200
Subject: [PATCH 127/207] Add Debug Option to Simulate state after Instant App
Update
---
.../dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java | 8 ++++++++
app/src/main/res/layout/fragment_debug.xml | 9 +++++++++
2 files changed, 17 insertions(+)
diff --git a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
index e577e3ec3..3a4d1e1d6 100644
--- a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
+++ b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
@@ -37,6 +37,7 @@
import org.dpppt.android.sdk.models.ExposureDay;
import ch.admin.bag.dp3t.R;
+import ch.admin.bag.dp3t.checkin.CrowdNotifierViewModel;
import ch.admin.bag.dp3t.checkin.models.SwissCovidAssociatedData;
import ch.admin.bag.dp3t.debug.model.DebugAppState;
import ch.admin.bag.dp3t.networking.CertificatePinning;
@@ -109,6 +110,13 @@ private void setupSdkViews(View view) {
getActivity().finish();
});
+ view.findViewById(R.id.debug_button_simulate_from_instant_app).setOnClickListener(v -> {
+ setDebugAppState(DebugAppState.NONE);
+ tracingViewModel.resetSdk();
+ SecureStorage.getInstance(requireContext()).setOnlyPartialOnboardingCompleted(true);
+ getActivity().finish();
+ });
+
CheckBox certPinningCheckbox = view.findViewById(R.id.debug_certificate_pinning);
certPinningCheckbox.setChecked(CertificatePinning.isEnabled());
certPinningCheckbox.setOnCheckedChangeListener((v, isChecked) -> {
diff --git a/app/src/main/res/layout/fragment_debug.xml b/app/src/main/res/layout/fragment_debug.xml
index 5f85fd838..b67e4b7a0 100644
--- a/app/src/main/res/layout/fragment_debug.xml
+++ b/app/src/main/res/layout/fragment_debug.xml
@@ -115,6 +115,15 @@
android:backgroundTint="@color/purple_main"
android:text="@string/reset_update_boarding" />
+
+
From ffdd9f1333826484e4a22590bba0ca7555aa400f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Tue, 1 Jun 2021 12:27:27 +0200
Subject: [PATCH 128/207] fix formatting and history type
---
.../java/ch/admin/bag/dp3t/util/NotificationUtil.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
index 571970fbc..6bc1db941 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationUtil.java
@@ -64,7 +64,7 @@ public static void generateContactNotification(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- if(DebugFragment.EXISTS){
+ if (DebugFragment.EXISTS) {
HistoryDatabase.getInstance(context).addEntry(
new HistoryEntry(
HistoryEntryType.NOTIFICATION, "Showing new message notification", false,
@@ -76,6 +76,7 @@ public static void generateContactNotification(Context context) {
NotificationRepeatWorker.startWorker(context);
}
+
public static void showReminderNotification(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(CHANNEL_ID_REMINDER, context.getString(R.string.android_reminder_channel_name),
@@ -86,10 +87,11 @@ public static void showReminderNotification(Context context) {
String message = context.getString(R.string.tracing_reminder_notification_subtitle);
Notification notification = createNotification(title, message, pendingIntent, CHANNEL_ID_REMINDER, context);
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);if(DebugFragment.EXISTS){
+ NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (DebugFragment.EXISTS) {
HistoryDatabase.getInstance(context).addEntry(
new HistoryEntry(
- HistoryEntryType.SYNC, "Showing activate tracing notification", false,
+ HistoryEntryType.NOTIFICATION, "Showing activate tracing notification", false,
System.currentTimeMillis()
)
);
From 8446f8824ed24257b74f3bffe56933edecd05835 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 13:01:14 +0200
Subject: [PATCH 129/207] Add Sync Button to Debug Fragment
---
.../dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java | 4 ++++
app/src/main/res/layout/fragment_debug.xml | 9 +++++++++
2 files changed, 13 insertions(+)
diff --git a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
index 3a4d1e1d6..e688c0bba 100644
--- a/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
+++ b/app/src/dev/java/ch/admin/bag/dp3t/debug/DebugFragment.java
@@ -50,6 +50,7 @@ public class DebugFragment extends Fragment {
public static final boolean EXISTS = true;
private TracingViewModel tracingViewModel;
+ private CrowdNotifierViewModel crowdNotifierViewModel;
public static void startDebugFragment(FragmentManager parentFragmentManager) {
parentFragmentManager.beginTransaction()
@@ -71,6 +72,7 @@ public DebugFragment() {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tracingViewModel = new ViewModelProvider(requireActivity()).get(TracingViewModel.class);
+ crowdNotifierViewModel = new ViewModelProvider(requireActivity()).get(CrowdNotifierViewModel.class);
}
@Override
@@ -110,6 +112,8 @@ private void setupSdkViews(View view) {
getActivity().finish();
});
+ view.findViewById(R.id.debug_button_sync_checkin_keys).setOnClickListener(v -> crowdNotifierViewModel.refreshTraceKeys());
+
view.findViewById(R.id.debug_button_simulate_from_instant_app).setOnClickListener(v -> {
setDebugAppState(DebugAppState.NONE);
tracingViewModel.resetSdk();
diff --git a/app/src/main/res/layout/fragment_debug.xml b/app/src/main/res/layout/fragment_debug.xml
index b67e4b7a0..e8e03bdc4 100644
--- a/app/src/main/res/layout/fragment_debug.xml
+++ b/app/src/main/res/layout/fragment_debug.xml
@@ -124,6 +124,15 @@
android:backgroundTint="@color/purple_main"
android:text="Simulate Update From Instant App" />
+
+
From 01d342617581f8b8d06b41d0087314e0dc0f35d7 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 14:31:31 +0200
Subject: [PATCH 130/207] Fix wrong torch icon
---
.../dp3t/checkin/checkinflow/QrCodeScannerFragment.java | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
index 7bfb3ff7b..83af1d99d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/QrCodeScannerFragment.java
@@ -125,15 +125,14 @@ private void setupFlashButton(Camera camera) {
camera.getCameraInfo().getTorchState().observe(getViewLifecycleOwner(), v -> {
if (v == TorchState.ON) {
- flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_light_off));
- } else {
flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_light_on));
+ } else {
+ flashButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_light_off));
}
});
- flashButton.setOnClickListener(v -> {
- camera.getCameraControl().enableTorch(camera.getCameraInfo().getTorchState().getValue() == TorchState.OFF);
- });
+ flashButton.setOnClickListener(v ->
+ camera.getCameraControl().enableTorch(camera.getCameraInfo().getTorchState().getValue() == TorchState.OFF));
}
private void startCameraAndQrAnalyzer() {
From ea975eb2c06d0660e98d40081a2e51d26b12e1cc Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 1 Jun 2021 16:06:15 +0200
Subject: [PATCH 131/207] removed deletion option in own events qr overview
list
---
.../generateqrcode/EventsOverviewFragment.kt | 13 -------------
.../dp3t/checkin/generateqrcode/QrCodeAdapter.kt | 9 +++++----
app/src/main/res/layout/item_qr_code.xml | 9 ---------
3 files changed, 5 insertions(+), 26 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
index 8487526e6..a26e132d1 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/EventsOverviewFragment.kt
@@ -32,11 +32,6 @@ class EventsOverviewFragment : Fragment() {
override fun onQrCodeClicked(qrCodeItem: VenueInfo) {
showFragment(QrCodeFragment.newInstance(qrCodeItem), modalAnimation = true)
}
-
- override fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo) {
- showDeleteConfirmationDialog(qrCodeItem)
- }
-
})
qrList.adapter = adapter
@@ -54,12 +49,4 @@ class EventsOverviewFragment : Fragment() {
}.root
}
- private fun showDeleteConfirmationDialog(venueInfo: VenueInfo) {
- AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
- .setMessage(R.string.delete_qr_code_dialog)
- .setPositiveButton(R.string.delete_button_title) { _, _ -> qrCodeViewModel.deleteQrCode(venueInfo) }
- .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
- .show()
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
index 4d63c35d9..dc65686b2 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
@@ -6,12 +6,15 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
-import ch.admin.bag.dp3t.checkin.generateqrcode.EventOverviewItem.Companion.TYPE_EXPLANATION
import ch.admin.bag.dp3t.checkin.generateqrcode.EventOverviewItem.Companion.TYPE_EVENT
+import ch.admin.bag.dp3t.checkin.generateqrcode.EventOverviewItem.Companion.TYPE_EXPLANATION
import ch.admin.bag.dp3t.checkin.generateqrcode.EventOverviewItem.Companion.TYPE_FOOTER
import ch.admin.bag.dp3t.checkin.generateqrcode.EventOverviewItem.Companion.TYPE_GENERATE_QR_CODE_BUTTON
+import ch.admin.bag.dp3t.databinding.ItemEventsExplanationBinding
+import ch.admin.bag.dp3t.databinding.ItemEventsFooterBinding
+import ch.admin.bag.dp3t.databinding.ItemGenerateQrCodeBinding
+import ch.admin.bag.dp3t.databinding.ItemQrCodeBinding
import ch.admin.bag.dp3t.extensions.getSubtitle
-import ch.admin.bag.dp3t.databinding.*
import org.crowdnotifier.android.sdk.model.VenueInfo
class QrCodeAdapter(private val onClickListener: OnClickListener) : RecyclerView.Adapter() {
@@ -63,7 +66,6 @@ class QrCodeAdapter(private val onClickListener: OnClickListener) : RecyclerView
qrCodeName.text = item.venueInfo.title
qrCodeLocation.setText(item.venueInfo.getSubtitle())
root.setOnClickListener { onClickListener.onQrCodeClicked(item.venueInfo) }
- qrCodeDelete.setOnClickListener { onClickListener.onDeleteQrCodeClicked(item.venueInfo) }
}
}
}
@@ -90,7 +92,6 @@ class QrCodeAdapter(private val onClickListener: OnClickListener) : RecyclerView
interface OnClickListener {
fun generateQrCode()
fun onQrCodeClicked(qrCodeItem: VenueInfo)
- fun onDeleteQrCodeClicked(qrCodeItem: VenueInfo)
}
diff --git a/app/src/main/res/layout/item_qr_code.xml b/app/src/main/res/layout/item_qr_code.xml
index 235a74c92..9e88a421d 100644
--- a/app/src/main/res/layout/item_qr_code.xml
+++ b/app/src/main/res/layout/item_qr_code.xml
@@ -43,15 +43,6 @@
-
-
\ No newline at end of file
From d832cb3d8ed0636cbcb993056b60a1ae2631e88d Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 1 Jun 2021 16:32:28 +0200
Subject: [PATCH 132/207] fixed 'checkin too long' message
---
.../admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt | 4 +---
.../ch/admin/bag/dp3t/checkin/diary/CheckinTimeHelper.kt | 7 +++++--
.../admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt | 4 +---
3 files changed, 7 insertions(+), 8 deletions(-)
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/checkinflow/CheckOutFragment.kt
index aba2145cd..bcf492396 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/checkinflow/CheckOutFragment.kt
@@ -18,7 +18,6 @@ import ch.admin.bag.dp3t.checkin.utils.NotificationHelper
import ch.admin.bag.dp3t.databinding.FragmentCheckOutAndEditBinding
import ch.admin.bag.dp3t.extensions.getSubtitle
import ch.admin.bag.dp3t.extensions.getSwissCovidLocationData
-import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.CrowdNotifier
import org.crowdnotifier.android.sdk.model.VenueInfo
@@ -97,8 +96,7 @@ class CheckOutFragment : Fragment() {
val checkinDuration = checkInState.checkOutTime - checkInState.checkInTime
val maxCheckinTime = checkInState.venueInfo.getSwissCovidLocationData().automaticCheckoutDelaylMs
if (checkinDuration > maxCheckinTime) {
- val maxDurationString = StringUtil.getShortDurationStringWithUnits(maxCheckinTime, requireContext())
- val dialogText = getString(R.string.checkout_too_long_alert_text).replace("{DURATION}", maxDurationString)
+ val dialogText = CheckinTimeHelper.getMaxCheckinTimeExceededMessage(maxCheckinTime, requireContext())
CheckinTimeHelper.showSavingNotPossibleDialog(dialogText, requireContext())
return
}
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/CheckinTimeHelper.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/CheckinTimeHelper.kt
index b8ed833b3..e9c1c6199 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/CheckinTimeHelper.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/CheckinTimeHelper.kt
@@ -1,12 +1,11 @@
package ch.admin.bag.dp3t.checkin.diary
import android.content.Context
-import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import ch.admin.bag.dp3t.R
import ch.admin.bag.dp3t.checkin.models.DiaryEntry
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
-import ch.admin.bag.dp3t.extensions.getSwissCovidLocationData
+import ch.admin.bag.dp3t.util.StringUtil
object CheckinTimeHelper {
@@ -32,5 +31,9 @@ object CheckinTimeHelper {
.show()
}
+ fun getMaxCheckinTimeExceededMessage(maxCheckinTime: Long, context: Context): String {
+ val maxDurationString = StringUtil.getShortDurationStringWithUnits(maxCheckinTime, context)
+ return context.getString(R.string.checkout_too_long_alert_text).replace("{DURATION}", maxDurationString)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
index 9d176220d..31526f59a 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/diary/EditDiaryEntryFragment.kt
@@ -12,7 +12,6 @@ import ch.admin.bag.dp3t.checkin.storage.DiaryStorage
import ch.admin.bag.dp3t.databinding.FragmentCheckOutAndEditBinding
import ch.admin.bag.dp3t.extensions.getSubtitle
import ch.admin.bag.dp3t.extensions.getSwissCovidLocationData
-import ch.admin.bag.dp3t.util.StringUtil
import org.crowdnotifier.android.sdk.CrowdNotifier
class EditDiaryEntryFragment : Fragment() {
@@ -80,8 +79,7 @@ class EditDiaryEntryFragment : Fragment() {
val checkinDuration = diaryEntry.departureTime - diaryEntry.arrivalTime
val maxCheckinTime = diaryEntry.venueInfo.getSwissCovidLocationData().automaticCheckoutDelaylMs
if (checkinDuration > maxCheckinTime) {
- val maxDurationString = StringUtil.getShortDurationStringWithUnits(checkinDuration, requireContext())
- val dialogText = getString(R.string.checkout_too_long_alert_text).replace("{DURATION}", maxDurationString)
+ val dialogText = CheckinTimeHelper.getMaxCheckinTimeExceededMessage(maxCheckinTime, requireContext())
CheckinTimeHelper.showSavingNotPossibleDialog(dialogText, requireContext())
return
}
From de566aab209f19b526d2033629c5089bc7083b71 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 17:15:51 +0200
Subject: [PATCH 133/207] Fix Checkin Selection in Report Flow
---
.../bag/dp3t/inform/ShareCheckinsFragment.kt | 17 ++++++++++++-----
.../main/res/layout/fragment_share_checkins.xml | 1 +
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
index 1b8265a42..2795e4e1e 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/ShareCheckinsFragment.kt
@@ -28,15 +28,16 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
adapter.setData(informViewModel.getSelectableCheckinItems())
adapter.itemSelectionListener { selectedItem, selected ->
informViewModel.setDiaryItemSelected(selectedItem.id, selected)
- refreshSendButtonEnabled()
+ refreshSendButtonAndAllCheckbox()
}
selectAllCheckbox.setOnCheckedChangeListener { _, isChecked ->
informViewModel.getSelectableCheckinItems().forEach {
informViewModel.setDiaryItemSelected(it.diaryEntry.id, isChecked)
}
adapter.setData(informViewModel.getSelectableCheckinItems())
- refreshSendButtonEnabled()
+ refreshSendButtonAndAllCheckbox()
}
+ selectAllContainer.setOnClickListener { selectAllCheckbox.isChecked = !selectAllCheckbox.isChecked }
dontSendButton.paintFlags = dontSendButton.paintFlags or Paint.UNDERLINE_TEXT_FLAG
dontSendButton.setOnClickListener {
if (informViewModel.hasSharedDP3TKeys) {
@@ -49,12 +50,18 @@ class ShareCheckinsFragment : TraceKeyShareBaseFragment() {
informViewModel.hasSharedCheckins = true
upload()
}
- refreshSendButtonEnabled()
+ refreshSendButtonAndAllCheckbox()
}.root
}
- private fun refreshSendButtonEnabled() {
- binding.sendButton.isEnabled = informViewModel.getSelectableCheckinItems().find { it.isSelected } != null
+ private fun refreshSendButtonAndAllCheckbox() {
+ val hasSelectedCheckins = informViewModel.getSelectableCheckinItems().any { it.isSelected }
+ val allCheckinsSelected = informViewModel.getSelectableCheckinItems().all { it.isSelected }
+ binding.apply {
+ binding.sendButton.isEnabled = hasSelectedCheckins
+ if (!hasSelectedCheckins) selectAllCheckbox.isChecked = false
+ if (allCheckinsSelected) selectAllCheckbox.isChecked = true
+ }
}
private fun upload() {
diff --git a/app/src/main/res/layout/fragment_share_checkins.xml b/app/src/main/res/layout/fragment_share_checkins.xml
index dba5940fe..898cb5ccd 100644
--- a/app/src/main/res/layout/fragment_share_checkins.xml
+++ b/app/src/main/res/layout/fragment_share_checkins.xml
@@ -29,6 +29,7 @@
android:text="@string/inform_share_checkins_subtitle" />
Date: Tue, 1 Jun 2021 18:03:55 +0200
Subject: [PATCH 134/207] correct checkmark icon for notifications card
---
.../dp3t/home/model/NotificationState.java | 19 +++++++++++++++++--
.../dp3t/util/NotificationStateHelper.java | 10 ++++++++--
app/src/main/res/layout/layout_status.xml | 8 ++++----
3 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java b/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
index ef321ad8c..6a0eef753 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/home/model/NotificationState.java
@@ -11,6 +11,7 @@
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import ch.admin.bag.dp3t.R;
@@ -50,7 +51,7 @@ public static int getText(NotificationState notificationState) {
public static int getIcon(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
- return R.drawable.ic_check;
+ return R.drawable.ic_check_circle;
case EXPOSED:
return R.drawable.ic_warning_round;
case POSITIVE_TESTED:
@@ -59,6 +60,20 @@ public static int getIcon(NotificationState notificationState) {
return -1;
}
+ @ColorRes
+ @Nullable
+ public static Integer getIconColor(NotificationState notificationState) {
+ switch (notificationState) {
+ case NO_REPORTS:
+ return null;
+ case EXPOSED:
+ return R.color.white;
+ case POSITIVE_TESTED:
+ return R.color.white;
+ }
+ return null;
+ }
+
@ColorRes
public static int getTitleTextColor(NotificationState notificationState) {
switch (notificationState) {
@@ -73,7 +88,7 @@ public static int getTitleTextColor(NotificationState notificationState) {
}
@ColorRes
- public static int geTextColor(NotificationState notificationState) {
+ public static int getTextColor(NotificationState notificationState) {
switch (notificationState) {
case NO_REPORTS:
return R.color.dark_main;
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationStateHelper.java b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationStateHelper.java
index f2b416133..6cfc551a5 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/util/NotificationStateHelper.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/util/NotificationStateHelper.java
@@ -33,10 +33,9 @@ public static void updateStatusView(View statusView, NotificationState state, lo
TextView textView = statusView.findViewById(R.id.status_text);
ImageView illustrationView = statusView.findViewById(R.id.status_illustration);
int titleColor = ContextCompat.getColor(context, NotificationState.getTitleTextColor(state));
- int textColor = ContextCompat.getColor(context, NotificationState.geTextColor(state));
+ int textColor = ContextCompat.getColor(context, NotificationState.getTextColor(state));
titleView.setTextColor(titleColor);
textView.setTextColor(textColor);
- iconView.setImageTintList(ColorStateList.valueOf(titleColor));
if (NotificationState.getTitle(state) != -1) {
titleView.setText(NotificationState.getTitle(state));
@@ -53,6 +52,13 @@ public static void updateStatusView(View statusView, NotificationState state, lo
if (NotificationState.getIcon(state) != -1) {
iconView.setImageResource(NotificationState.getIcon(state));
iconView.setVisibility(View.VISIBLE);
+
+ Integer iconColor = NotificationState.getIconColor(state);
+ if (iconColor != null) {
+ iconView.setImageTintList(ColorStateList.valueOf(ContextCompat.getColor(context, iconColor)));
+ } else {
+ iconView.setImageTintList(null);
+ }
} else {
iconView.setVisibility(View.GONE);
}
diff --git a/app/src/main/res/layout/layout_status.xml b/app/src/main/res/layout/layout_status.xml
index 13c7b652b..c04f7a090 100644
--- a/app/src/main/res/layout/layout_status.xml
+++ b/app/src/main/res/layout/layout_status.xml
@@ -32,14 +32,14 @@
+ tools:src="@drawable/ic_check_circle" />
Date: Tue, 1 Jun 2021 19:13:24 +0200
Subject: [PATCH 135/207] Change oldestSharedKeyDate to
oldestSharedKeyDateOrCheckin
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 2 +
.../admin/bag/dp3t/inform/InformViewModel.kt | 49 ++++++++++++-------
.../admin/bag/dp3t/reports/ReportsFragment.kt | 3 +-
.../admin/bag/dp3t/storage/SecureStorage.java | 11 +++++
4 files changed, 47 insertions(+), 18 deletions(-)
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 2c7e7b2f1..30a30ad50 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
@@ -459,6 +459,7 @@ private void setupCovidCodeCard() {
tracingStatusInterface.resetInfectionStatus(getContext());
secureStorage.setIsolationEndDialogTimestamp(-1L);
secureStorage.setPositiveReportOldestSharedKey(-1L);
+ secureStorage.setPositiveReportOldestSharedKeyOrCheckin(-1L);
})
.setNegativeButton(R.string.cancel, (dialog, id) -> {
//do nothing
@@ -542,6 +543,7 @@ public void onChanged(TracingStatusInterface tracingStatusInterface) {
tracingStatusInterface.resetInfectionStatus(getContext());
secureStorage.setIsolationEndDialogTimestamp(-1L);
secureStorage.setPositiveReportOldestSharedKey(-1L);
+ secureStorage.setPositiveReportOldestSharedKeyOrCheckin(-1L);
})
.setNegativeButton(R.string.answer_no, (dialog, which) -> {
long newTimestamp = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
diff --git a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
index b3ad7ab2b..ba43c9865 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/inform/InformViewModel.kt
@@ -35,7 +35,9 @@ import java.util.*
import java.util.concurrent.CancellationException
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
+import kotlin.math.max
private const val TIMEOUT_VALID_CODE = 1000L * 60 * 5
private const val MAX_EXPOSURE_AGE_MILLIS = 10 * 24 * 60 * 60 * 1000L
@@ -104,6 +106,8 @@ class InformViewModel(application: Application, private val state: SavedStateHan
val onsetResponseTime = onsetResponseTime ?: System.currentTimeMillis()
val timeBetweenOnsetAndUploadRequest = (System.currentTimeMillis() - onsetResponseTime).toInt()
delay(UPLOAD_REQUEST_TIME_PADDING - (System.currentTimeMillis() - onsetResponseTime) % UPLOAD_REQUEST_TIME_PADDING)
+ var oldestSharedKey: Long? = null
+ var oldestSharedCheckin: Long? = null
try {
loadAccessTokens(covidCode)
} catch (exception: Throwable) {
@@ -116,7 +120,7 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
try {
if (hasSharedDP3TKeys) {
- uploadTEKs()
+ oldestSharedKey = uploadTEKs()
} else {
performFakeTEKUpload()
}
@@ -131,7 +135,11 @@ class InformViewModel(application: Application, private val state: SavedStateHan
}
try {
- performCheckinsUpload(timeBetweenOnsetAndUploadRequest, isFake = !hasSharedCheckins)
+ if (hasSharedCheckins) {
+ oldestSharedCheckin = performCheckinsUpload(timeBetweenOnsetAndUploadRequest)
+ } else {
+ performFakeCheckinsUpload(timeBetweenOnsetAndUploadRequest)
+ }
} catch (exception: Throwable) {
when (exception) {
is HttpException -> emit(Resource.error(InformRequestError.USER_UPLOAD_NETWORK_ERROR, exception))
@@ -144,12 +152,14 @@ class InformViewModel(application: Application, private val state: SavedStateHan
appConfigManager.iAmInfected = true
appConfigManager.iAmInfectedIsResettable = true
DP3T.stop(getApplication())
+ secureStorage.positiveReportOldestSharedKeyOrCheckin = listOfNotNull(oldestSharedKey, oldestSharedCheckin).minOrNull() ?: -1
emit(Resource.success(data = null))
}
fun getSelectableCheckinItems(): List {
return diaryStorage.entries.filter {
it.venueInfo.getSwissCovidLocationData().type == VenueType.USER_QR_CODE && it.departureTime >= onsetDate ?: 0
+ && it.departureTime > System.currentTimeMillis() - MAX_EXPOSURE_AGE_MILLIS
}.map {
SelectableCheckinItem(it, isSelected = selectedDiaryEntryIds.contains(it.id))
}
@@ -176,20 +186,21 @@ class InformViewModel(application: Application, private val state: SavedStateHan
DP3TKotlin.sendFakeInfectedRequest(getApplication(), authorizationHeader)
}
- private suspend fun uploadTEKs() {
+ /**
+ * Returns the oldestSharedKeyDate
+ */
+ private suspend fun uploadTEKs(): Long {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastDP3TInformToken)
val onsetDate = JwtUtil.getOnsetDate(secureStorage.lastDP3TInformToken)
- var error: Throwable? = null
-
// Wrapping traditional callback in a suspendCoroutine
- suspendCoroutine { continuation ->
+ return suspendCoroutine { continuation ->
pendingUploadTask?.performUpload(getApplication(), onsetDate, ExposeeAuthMethodAuthorization(authorizationHeader),
object : ResponseCallback {
override fun onSuccess(oldestSharedKeyDayDate: DayDate) {
// Store the oldest shared Key date of this report (but at least now-MAX_EXPOSURE_AGE_MILLIS)
- val oldestSharedKeyDate = Math.max(
+ val oldestSharedKeyDate = max(
oldestSharedKeyDayDate.startOfDayTimestamp,
System.currentTimeMillis() - MAX_EXPOSURE_AGE_MILLIS
)
@@ -200,25 +211,29 @@ class InformViewModel(application: Application, private val state: SavedStateHan
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(ISOLATION_DURATION_DAYS)
secureStorage.isolationEndDialogTimestamp = isolationEndDialogTimestamp
hasSharedDP3TKeys = true
- continuation.resume(Unit)
+ continuation.resume(oldestSharedKeyDate)
}
override fun onError(throwable: Throwable) {
- error = throwable
- continuation.resume(Unit)
+ continuation.resumeWithException(throwable)
}
})
}
- error?.let { throw(it) }
}
- private suspend fun performCheckinsUpload(timeBetweenOnsetAndUploadRequest: Int, isFake: Boolean) {
+ /**
+ * Returns oldest shared Checkin
+ */
+ private suspend fun performCheckinsUpload(timeBetweenOnsetAndUploadRequest: Int): Long? {
val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
- if (isFake) {
- userUploadRepository.fakeUserUpload(timeBetweenOnsetAndUploadRequest, authorizationHeader)
- } else {
- userUploadRepository.userUpload(getUploadVenueInfos(), timeBetweenOnsetAndUploadRequest, authorizationHeader)
- }
+ val uploadVenueInfos = getUploadVenueInfos()
+ userUploadRepository.userUpload(uploadVenueInfos, timeBetweenOnsetAndUploadRequest, authorizationHeader)
+ return uploadVenueInfos.minOfOrNull { it.intervalStartMs }
+ }
+
+ private suspend fun performFakeCheckinsUpload(timeBetweenOnsetAndUploadRequest: Int) {
+ val authorizationHeader = getAuthorizationHeader(secureStorage.lastCheckinInformToken)
+ userUploadRepository.fakeUserUpload(timeBetweenOnsetAndUploadRequest, authorizationHeader)
}
private suspend fun loadOnsetDate(covidcode: String) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
index 4e3a631b4..fbc2211c4 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/reports/ReportsFragment.kt
@@ -213,7 +213,7 @@ class ReportsFragment : Fragment() {
}
}
State.POSITIVE_TESTED -> {
- val oldestSharedKeyDateMillis = secureStorage.positiveReportOldestSharedKey
+ val oldestSharedKeyDateMillis = secureStorage.positiveReportOldestSharedKeyOrCheckin
binding.reportsInfected.apply {
faqButton.setOnClickListener { showFaq() }
cardEncountersFaqWhoIsNotifiedContainer.isVisible = oldestSharedKeyDateMillis > 0L
@@ -256,6 +256,7 @@ class ReportsFragment : Fragment() {
tracingViewModel.appStatusLiveData.value?.resetInfectionStatus(context)
secureStorage.isolationEndDialogTimestamp = -1L
secureStorage.positiveReportOldestSharedKey = -1L
+ secureStorage.positiveReportOldestSharedKeyOrCheckin = -1L
parentFragmentManager.popBackStack()
}
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
diff --git a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
index 91d27eef4..868693e6b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/storage/SecureStorage.java
@@ -78,6 +78,9 @@ public class SecureStorage {
private static final String KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG = "KEY_CROWD_NOTIFIER_LAST_KEY_BUNDLE_TAG";
private static final String KEY_LAST_SUCCESSFUL_CHECKIN_DOWNLOAD = "KEY_LAST_SUCCESSFUL_CHECKIN_DOWNLOAD";
private static final String KEY_ONLY_PARTIAL_ONBOARDING_DONE = "KEY_ONLY_PARTIAL_ONBOARDING_DONE";
+ private static final String KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY_OR_CHECKIN =
+ "KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY_OR_CHECKIN";
+
private static SecureStorage instance;
@@ -384,6 +387,14 @@ public void setPositiveReportOldestSharedKey(long setPositiveReportOldestSharedK
prefs.edit().putLong(KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY, setPositiveReportOldestSharedKey).apply();
}
+ public long getPositiveReportOldestSharedKeyOrCheckin() {
+ return prefs.getLong(KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY_OR_CHECKIN, -1L);
+ }
+
+ public void setPositiveReportOldestSharedKeyOrCheckin(long oldestSharedKeyOrCheckin) {
+ prefs.edit().putLong(KEY_POSITIVE_REPORT_OLDEST_SHARED_KEY_OR_CHECKIN, oldestSharedKeyOrCheckin).apply();
+ }
+
private synchronized SharedPreferences initializeSharedPreferences(@NonNull Context context) {
try {
return createEncryptedSharedPreferences(context);
From fb05e6d15c2eaf818ef0dcaf3334792b2f151267 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 19:26:20 +0200
Subject: [PATCH 136/207] Implemented Error Popup when entering Covidcode while
checked in
---
.../ch/admin/bag/dp3t/home/HomeFragment.java | 17 ++-
common/src/main/res/values-bs/strings.xml | 9 ++
common/src/main/res/values-de/strings.xml | 9 ++
common/src/main/res/values-es/strings.xml | 9 ++
common/src/main/res/values-fr/strings.xml | 114 ++++++++++--------
common/src/main/res/values-hr/strings.xml | 9 ++
common/src/main/res/values-it/strings.xml | 9 ++
common/src/main/res/values-pt/strings.xml | 9 ++
common/src/main/res/values-rm/strings.xml | 9 ++
common/src/main/res/values-sq/strings.xml | 9 ++
common/src/main/res/values-sr/strings.xml | 9 ++
common/src/main/res/values-ti/strings.xml | 9 ++
common/src/main/res/values-tr/strings.xml | 9 ++
common/src/main/res/values/strings.xml | 9 ++
14 files changed, 185 insertions(+), 54 deletions(-)
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 30a30ad50..8ef96b738 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
@@ -472,13 +472,26 @@ private void setupCovidCodeCard() {
covidCodeTitle.setText(R.string.home_covidcode_card_title);
covidCodeText.setText(R.string.home_covidcode_card_text);
covidCodeButton.setOnClickListener(v -> {
- Intent intent = new Intent(getActivity(), InformActivity.class);
- startActivity(intent);
+ if (crowdNotifierViewModel.isCheckedIn().getValue()) {
+ showCannotEnterCovidcodeWhileCheckedInDialog();
+ } else {
+ Intent intent = new Intent(getActivity(), InformActivity.class);
+ startActivity(intent);
+ }
});
}
});
}
+ private void showCannotEnterCovidcodeWhileCheckedInDialog() {
+ new AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.error_cannot_enter_covidcode_while_checked_in)
+ .setPositiveButton(R.string.checkout_button_title, (dialog, id) -> showCheckOutFragment())
+ .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss())
+ .create()
+ .show();
+ }
+
private void showCheckOutFragment() {
requireActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter,
diff --git a/common/src/main/res/values-bs/strings.xml b/common/src/main/res/values-bs/strings.xml
index 6d709a2ab..7c2af5ec0 100644
--- a/common/src/main/res/values-bs/strings.xml
+++ b/common/src/main/res/values-bs/strings.xml
@@ -1753,4 +1753,13 @@ U tom slučaju kontaktirajte nas ovde:"
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 09fb2a557..5a48570be 100644
--- a/common/src/main/res/values-de/strings.xml
+++ b/common/src/main/res/values-de/strings.xml
@@ -1836,4 +1836,13 @@ Dieser Titel wird beim Check-in angezeigt."
"https://www.bag.admin.ch/bag/de/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""Für diesen Check-In gilt eine maximale Check-in Dauer von {DURATION}. Bitte überprüfen Sie nochmals Ihre Angabe."
+
+
+ "Licht an"
+
+
+ "Licht aus"
+
+
+ "Sie sind noch eingecheckt. Bitte checken Sie aus bevor Sie den Covidcode eingeben."
\ 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 77773d607..6f4f5669d 100644
--- a/common/src/main/res/values-es/strings.xml
+++ b/common/src/main/res/values-es/strings.xml
@@ -1745,4 +1745,13 @@ Posibles utilizaciones:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 057cc8715..e3e171ce9 100644
--- a/common/src/main/res/values-fr/strings.xml
+++ b/common/src/main/res/values-fr/strings.xml
@@ -260,7 +260,7 @@ Ainsi, vous contribuez à interrompre les chaînes de transmission."
"Suivez les consignes reçues sur l'isolement."
- "Contacts et connexion désactivés"
+ "Contacts et check-ins désactivés""Aucune contact n’est sauvegardé.""Contactez""Suivez"
@@ -624,7 +624,7 @@ Ceci permet de garantir que seuls les cas confirmés sont déclarés dans l'appl
"Qu'est-ce qui est envoyé ?"
- "Seules les clés privées de votre application et les identifiants codés de vos connexions sont transmises, aucune donnée personnelle.
+ "Seules les clés privées de votre application et les identifiants codés de vos check-ins sont transmises, aucune donnée personnelle.
Ainsi, d’autres utilisateurs de l’application SwissCovid apprennent qu’il y a un risque d’infection.
"
@@ -1426,16 +1426,16 @@ Appelez :""Dans 1 jour"
- "Rappel de déconnexion"
+ "Rappel de check-out""Souhaitez-vous vous déconnecter ?"
- "Déconnexion automatique"
+ "Check-out automatique"
- "Il y a eu une déconnexion automatique."
+ "Il y a eu un check-out automatique.""Scanner le code QR et se connecter"
@@ -1459,7 +1459,7 @@ Appelez :"
"Fin"
- "Rappel de déconnexion"
+ "Rappel de check-out""Arrêter"
@@ -1477,10 +1477,10 @@ Appelez :"
"L'application doit avoir accès à l'appareil photo pour pouvoir scanner le code QR."
- "Mes connexions"
+ "Mes check-ins"
- "Connexion"
+ "Check-in""L’appareil photo est nécessaire pour scanner le code QR."
@@ -1492,19 +1492,19 @@ Appelez :"
"Veuillez vous authentifier pour accéder au journal"
- "Déconnexion"
+ "Check-out"
- "Connexion"
+ "Check-in"
- "Connexion"
+ "Check-in""Créer un code QR"
- "Proposer des connexions ?"
+ "Créer un code check-in?""Se connecter "
@@ -1513,7 +1513,7 @@ Appelez :"
"Le rappel n’a pas pu être activé."
- "Afin de recevoir un rappel de déconnexion, vous devez autoriser la fonction \\"Notifications\\" dans les paramètres de l’application."
+ "Afin de recevoir un rappel de check-out, vous devez autoriser la fonction \\"Notifications\\" dans les paramètres de l’application.""Adapter"
@@ -1528,7 +1528,7 @@ Appelez :"
"Si vous scannez un code QR SwissCovid pour vous connecter sur place, une donnée est automatiquement enregistrée."
- "SwissCovid doit avoir accès à l’appareil photo de votre smartphone. L’appareil photo est uniquement utilisé pour scanner les codes QR."
+ "SwissCovid doit avoir accès à l’appareil photo de votre smartphone. L’appareil photo est uniquement utilisé pour scanner les QR codes.""Autoriser l’accès à l’appareil photo"
@@ -1550,14 +1550,14 @@ Appelez :"
"Erreur""Le code QR n’est pas encore valable.""Le code QR n’est plus valable."
- "Votre connexion a déjà été établie."
+ "Votre check-in a déjà été établie.""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 ""Déconnecter""Fonction de rappel"
- "Notification de connexion permanente"
- "Connexion activée depuis {TIME} heures."
+ "Notification de check-in permanente"
+ "Check-in activé depuis {TIME} heures.""Sauvegarder"
@@ -1566,19 +1566,19 @@ Appelez :"
"Scanner le code QR"
- "Connexion désactivée"
+ "Check-in désactivée"
- "Pas de connexion possible actuellement"
+ "Pas de check-in possible actuellement"
- "Scannez un code QR SwissCovid pour vous connecter sur place."
+ "Scannez un QR code SwissCovid pour vous connecter sur place.""Si vous scannez un code QR SwissCovid pour vous connecter sur place, une donnée est automatiquement enregistrée."
- "Connexion effectuée"
+ "Check-in effectué""Du {DATE1} au {DATE2}""Plus d’informations sur le code COVID"
@@ -1595,13 +1595,13 @@ Appelez :"
"Imprimer"
- "Scannez un code QR SwissCovid pour vous connecter sur place."
+ "Scannez un QR code SwissCovid pour vous connecter sur place."
- "Partager les données de connexion"
+ "Partager les données de check-ins"
- "Si vous partagez vos données de connexion, l’application peut notifier les personnes qui étaient connectées au même endroit et à la même heure."
+ "Si vous partagez vos données de check-in, l’application peut notifier les personnes qui étaient connectées au même endroit et à la même heure.""Sélectionner tous"
@@ -1626,7 +1626,7 @@ Appelez :"
"Test positif ?"
- "Réactiver contacts et connexions."
+ "Réactiver contacts et check-ins.""{HOURS} h {MINUTES}'"
@@ -1638,25 +1638,25 @@ Appelez :"
"Créez un code QR permettant aux utilisateurs de se connecter sur place."
- "Souhaitez-vous vraiment supprimer le code QR ?"
+ "Souhaitez-vous vraiment supprimer le QR code?"
- "Auto-connexion"
+ "Auto-check-in"
- "Scannez un code QR SwissCovid pour vous connecter sur place."
+ "Scannez un QR code SwissCovid pour vous connecter sur place.""Sauvegarde impossible"
- "Les heures sélectionnées coïncident avec d’autres connexions dans votre journal. Vérifiez encore une fois votre saisie."
+ "Les heures sélectionnées coïncident avec d’autres check-ins dans votre journal. Vérifiez encore une fois votre saisie."
- "Les données de connexion sélectionnées ont été transmises. Toutes les personnes qui étaient connectées au même endroit à la même heure seront notifiées."
+ "Les données de check-in sélectionnées ont été transmises. Toutes les personnes qui étaient connectées au même endroit à la même heure seront notifiées."
- "Connexion SwissCovid"
+ "SwissCovid Check-in""Se connecter sans enregistrer de données"
@@ -1680,19 +1680,19 @@ Appelez :"
"Fin de l’isolement ?"
- "Souhaitez-vous proposer des connexions ?"
- "À quoi sert la fonction de connexion ?"
- "La fonction de connexion sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment."
+ "Souhaitez-vous créer un code de check-in?"
+ "À quoi sert la fonction de check-in?"
+ "La fonction de check-in sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment.""Pas de sauvegarde centralisée des données"
- "La fonction de connexion SwissCovid ne nécessite pas la saisie de coordonnées comme le nom, l’adresse ou le numéro de téléphone et elle n’enregistre pas les données de géolocalisation. La liste des connexions est enregistrée localement sur votre smartphone. Les données locales sont effacées après 14 jours."
+ "La fonction des SwissCovid check-ins ne nécessite pas la saisie de coordonnées comme le nom, l’adresse ou le numéro de téléphone et elle n’enregistre pas les données de géolocalisation. La liste des check-ins est enregistrée localement sur votre smartphone. Les données locales sont effacées après 14 jours.""Créer un code QR""Au moyen du code QR SwissCovid, il est possible de se connecter sur place sans devoir enregistrer ses données.""Cette fonction ne remplace pas la collecte obligatoire des coordonnées."
- "La connexion SwissCovid ne remplace la collecte des coordonnées prescrite par l’« ordonnance COVID-19 situation particulière »."
- "Où la fonction de connexion peut-elle être utilisée ?"
- "À quoi sert la fonction de connexion ?"
- "La fonction de connexion SwissCovid sera utile lors de rencontres dans des lieux où la collecte des coordonnées n’est pas obligatoire. Il peut s’agir – de rencontres privées – d’événements organisés par des associations – de rassemblements dans le cadre professionnel, p. ex. salles de réunion, bureaux, cantines, etc."
- "La fonction de connexion sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment."
+ "La check-ins SwissCovid ne remplace la collecte des coordonnées prescrite par l’« ordonnance COVID-19 situation particulière »."
+ "Où la fonction de check-in peut-elle être utilisée?"
+ "À quoi sert la fonction de check-in?"
+ "La fonction de check-in SwissCovid sera utile lors de rencontres dans des lieux où la collecte des coordonnées n’est pas obligatoire. Il peut s’agir – de rencontres privées – d’événements organisés par des associations – de rassemblements dans le cadre professionnel, p. ex. salles de réunion, bureaux, cantines, etc."
+ "La fonction de check-in sans enregistrement des coordonnées complète l’application SwissCovid. En cas de test positif, elle vous permet d’avertir non seulement vos proches, mais aussi les personnes qui étaient présentes au même endroit au même moment.""Situations à risque"
@@ -1701,28 +1701,29 @@ Appelez :"
"Contacts"
- "Connexion"
+ "Check-in""Que dois-je faire ?"
- "Connexion positive"
+ "Check-in positif"
- "Connexions / contacts positifs"
+ "Check-ins / contacts positifs"
- "Code QR de l’utilisateur"
+ "QR code de l’utilisateur"
- "Code QR de traçage des contacts"
+ "QR code de traçage des contacts"
- "Après un test positif, la fonction de connexion est automatiquement désactivée. Elle peut être réactivée après la fin de l’isolement."
+ "Après un test positif, la fonction de check-in est automatiquement désactivée. Elle peut être réactivée après la fin de l’isolement."
- "Saisissez un titre qui décrit le lieu ou l’événement. Ce titre s’affichera lors de la connexion."
+ "Saisissez un titre qui décrit le lieu ou l’événement.
+Ce titre s’affichera lors du check-in.""Que pouvez-vous faire ?""Protéger les contacts""Observer les symptômes"
@@ -1732,21 +1733,21 @@ Appelez :"
"Faites-vous immédiatement tester si vous présentez des symptômes. Même en l’absence de symptômes il peut être utile et recommandé de se faire tester pour le coronavirus."
- "Connexion"
+ "Check-in""Pas de données transmises""Aucune clé privée n’a été transmise.""Retour""Ne pas transmettre de données"
- "Aucune donnée de connexion n’a été transmise."
+ "Aucune donnée de check-in n’a été transmise.""Partager la sélection""Ne rien partager""Se connecter sans enregistrer de données"
- "Connexions"
- "La fonction de connexion SwissCovid vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre."
+ "Check-ins"
+ "La fonction de SwissCovid check-in vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre.""Votre présence est seulement enregistrée dans votre application. Il n’est pas nécessaire de saisir vos coordonnées dans l’application."
- "La fonction de connexion SwissCovid vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre. Votre présence est seulement enregistrée dans votre application. Il n’est pas nécessaire de saisir vos coordonnées dans l’application. À cette fin, les conditions d’utilisation et la déclaration de protection des données ont été mises à jour."
+ "La fonction de SwissCovid check-in vous permet de vous connecter via l’application dans un lieu particulier ou lors d’une rencontre. Votre présence est seulement enregistrée dans votre application. Il n’est pas nécessaire de saisir vos coordonnées dans l’application. À cette fin, les conditions d’utilisation et la déclaration de protection des données ont été mises à jour.""Passer à l'étape suivante "
@@ -1776,4 +1777,13 @@ Appelez :"
"https://www.bag.admin.ch/bag/fr/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 1fd361d7a..3fda32aec 100644
--- a/common/src/main/res/values-hr/strings.xml
+++ b/common/src/main/res/values-hr/strings.xml
@@ -1751,4 +1751,13 @@ U tom slučaju kontaktirajte nas ovde:"
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 5e6fc52bc..c31223e43 100644
--- a/common/src/main/res/values-it/strings.xml
+++ b/common/src/main/res/values-it/strings.xml
@@ -1788,4 +1788,13 @@ Possibili luoghi d'impiego:
"https://www.bag.admin.ch/bag/it/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 800bfe8d2..80ef32410 100644
--- a/common/src/main/res/values-pt/strings.xml
+++ b/common/src/main/res/values-pt/strings.xml
@@ -1755,4 +1755,13 @@ Este título vai ser indicado no check-in."
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 3de7f0790..233257bac 100644
--- a/common/src/main/res/values-rm/strings.xml
+++ b/common/src/main/res/values-rm/strings.xml
@@ -1753,4 +1753,13 @@ Possible locations:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 8912c8f0d..56c10eda6 100644
--- a/common/src/main/res/values-sq/strings.xml
+++ b/common/src/main/res/values-sq/strings.xml
@@ -1756,4 +1756,13 @@ Ky titull shfaqet gjatë Check-in."
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 a4162b5f0..3f34b1491 100644
--- a/common/src/main/res/values-sr/strings.xml
+++ b/common/src/main/res/values-sr/strings.xml
@@ -1751,4 +1751,13 @@ U tom slučaju kontaktirajte nas ovde:"
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 db660c206..ddccd13f7 100644
--- a/common/src/main/res/values-ti/strings.xml
+++ b/common/src/main/res/values-ti/strings.xml
@@ -1739,4 +1739,13 @@ SwissCovid ነቶም ኣብቶም ተቓደውቲ ሃገራት ዝተዘርገ
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 52a2456e7..5995220f6 100644
--- a/common/src/main/res/values-tr/strings.xml
+++ b/common/src/main/res/values-tr/strings.xml
@@ -1739,4 +1739,13 @@ Bu başlık check-in sırasında gösterilir."
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ 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 db87b7d88..82920899c 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -1777,4 +1777,13 @@ Possible locations:
"https://www.bag.admin.ch/bag/en/home/krankheiten/ausbrueche-epidemien-pandemien/aktuelle-ausbrueche-epidemien/novel-cov/testen.html""For this check-in a maximum check-in duration of {DURATION} applies. Please check your input again."
+
+
+ "turn light on"
+
+
+ "turn light off"
+
+
+
\ No newline at end of file
From 881e676da75627849517ee78e3cf6029df433811 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 19:37:04 +0200
Subject: [PATCH 137/207] Set QR Code title length to max 60 letters
---
app/src/main/res/layout/fragment_generate_qr_code.xml | 1 +
common/src/main/res/values-bs/strings.xml | 2 +-
common/src/main/res/values-fr/strings.xml | 2 +-
common/src/main/res/values-hr/strings.xml | 2 +-
common/src/main/res/values-it/strings.xml | 2 +-
common/src/main/res/values-pt/strings.xml | 2 +-
common/src/main/res/values-sq/strings.xml | 2 +-
common/src/main/res/values-sr/strings.xml | 2 +-
common/src/main/res/values-tr/strings.xml | 2 +-
9 files changed, 9 insertions(+), 8 deletions(-)
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 387feac47..8fb7d683f 100644
--- a/app/src/main/res/layout/fragment_generate_qr_code.xml
+++ b/app/src/main/res/layout/fragment_generate_qr_code.xml
@@ -81,6 +81,7 @@
android:layout_height="wrap_content"
android:hint="@string/web_generator_title_placeholder"
android:inputType="textNoSuggestions"
+ android:maxLength="60"
android:textColor="@color/blue_main"
android:textStyle="bold" />
diff --git a/common/src/main/res/values-bs/strings.xml b/common/src/main/res/values-bs/strings.xml
index 7c2af5ec0..d791324c9 100644
--- a/common/src/main/res/values-bs/strings.xml
+++ b/common/src/main/res/values-bs/strings.xml
@@ -1434,7 +1434,7 @@ U tom slučaju kontaktirajte nas ovde:"
"Podsećanje na prijavu"
- "Isključeno"
+ "Off""Identifikujte se, kako biste pristupili dnevniku"
diff --git a/common/src/main/res/values-fr/strings.xml b/common/src/main/res/values-fr/strings.xml
index e3e171ce9..4d1349dc3 100644
--- a/common/src/main/res/values-fr/strings.xml
+++ b/common/src/main/res/values-fr/strings.xml
@@ -1462,7 +1462,7 @@ Appelez :"
"Rappel de check-out"
- "Arrêter"
+ "Off""Veuillez vous authentifier pour accéder au journal"
diff --git a/common/src/main/res/values-hr/strings.xml b/common/src/main/res/values-hr/strings.xml
index 3fda32aec..1db1217cc 100644
--- a/common/src/main/res/values-hr/strings.xml
+++ b/common/src/main/res/values-hr/strings.xml
@@ -1432,7 +1432,7 @@ U tom slučaju kontaktirajte nas ovde:"
"Podsećanje na prijavu"
- "Isključeno"
+ "Off""Identifikujte se, kako biste pristupili dnevniku"
diff --git a/common/src/main/res/values-it/strings.xml b/common/src/main/res/values-it/strings.xml
index c31223e43..b28692262 100644
--- a/common/src/main/res/values-it/strings.xml
+++ b/common/src/main/res/values-it/strings.xml
@@ -1468,7 +1468,7 @@ Chiama il seguente numero:"
"Promemoria check-out"
- "Disattiva"
+ "Off""Autenticati per accedere al diario"
diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml
index 80ef32410..f1132129b 100644
--- a/common/src/main/res/values-pt/strings.xml
+++ b/common/src/main/res/values-pt/strings.xml
@@ -1435,7 +1435,7 @@ Ligue para:"
"Lembrete de checkout"
- "Desligado"
+ "Off""Autentique-se para ter acesso ao diário"
diff --git a/common/src/main/res/values-sq/strings.xml b/common/src/main/res/values-sq/strings.xml
index 56c10eda6..2400b7e9d 100644
--- a/common/src/main/res/values-sq/strings.xml
+++ b/common/src/main/res/values-sq/strings.xml
@@ -1435,7 +1435,7 @@ Atëherë na telefononi këtu:"
"Kujtesa e Check-out"
- "Fikur"
+ "Off""Autentifikohuni për aksesin në ditar"
diff --git a/common/src/main/res/values-sr/strings.xml b/common/src/main/res/values-sr/strings.xml
index 3f34b1491..76921071d 100644
--- a/common/src/main/res/values-sr/strings.xml
+++ b/common/src/main/res/values-sr/strings.xml
@@ -1432,7 +1432,7 @@ U tom slučaju kontaktirajte nas ovde:"
"Podsećanje na prijavu"
- "Isključeno"
+ "Off""Identifikujte se, kako biste pristupili dnevniku"
diff --git a/common/src/main/res/values-tr/strings.xml b/common/src/main/res/values-tr/strings.xml
index 5995220f6..f9dc0e63e 100644
--- a/common/src/main/res/values-tr/strings.xml
+++ b/common/src/main/res/values-tr/strings.xml
@@ -1418,7 +1418,7 @@ O halde şu numarayı arayın:"
"Check-out hatırlatması"
- "Kapalı"
+ "Off""Günlüğe erişim için kendinizi yetkilendirin"
From 036d1951e195722d57098ed2a635557e1c463c2a Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Tue, 1 Jun 2021 19:45:30 +0200
Subject: [PATCH 138/207] Remove subtitle from generated QR-Code List
---
.../checkin/generateqrcode/QrCodeAdapter.kt | 1 -
app/src/main/res/layout/item_qr_code.xml | 25 +++++--------------
2 files changed, 6 insertions(+), 20 deletions(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
index 4d63c35d9..a05bfa4b7 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QrCodeAdapter.kt
@@ -61,7 +61,6 @@ class QrCodeAdapter(private val onClickListener: OnClickListener) : RecyclerView
item as EventItem
binding.apply {
qrCodeName.text = item.venueInfo.title
- qrCodeLocation.setText(item.venueInfo.getSubtitle())
root.setOnClickListener { onClickListener.onQrCodeClicked(item.venueInfo) }
qrCodeDelete.setOnClickListener { onClickListener.onDeleteQrCodeClicked(item.venueInfo) }
}
diff --git a/app/src/main/res/layout/item_qr_code.xml b/app/src/main/res/layout/item_qr_code.xml
index 235a74c92..8d933421f 100644
--- a/app/src/main/res/layout/item_qr_code.xml
+++ b/app/src/main/res/layout/item_qr_code.xml
@@ -11,6 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_large"
+ android:gravity="center_vertical"
android:orientation="horizontal">
-
-
-
-
-
-
-
+ tools:text="UB E21.7" />
Date: Tue, 1 Jun 2021 19:57:44 +0200
Subject: [PATCH 139/207] Add Signature Check to Checkin Trace Keys Repository
---
.../bag/dp3t/checkin/networking/TraceKeysRepository.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
index 61aaad22f..a66829f52 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/TraceKeysRepository.java
@@ -3,13 +3,16 @@
import android.content.Context;
import java.io.IOException;
+import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import org.crowdnotifier.android.sdk.model.DayDate;
import org.crowdnotifier.android.sdk.model.ProblematicEventInfo;
import org.dpppt.android.sdk.DP3T;
+import org.dpppt.android.sdk.backend.SignatureVerificationInterceptor;
import org.dpppt.android.sdk.backend.UserAgentInterceptor;
+import org.dpppt.android.sdk.util.SignatureUtil;
import org.jetbrains.annotations.NotNull;
import ch.admin.bag.dp3t.BuildConfig;
@@ -37,6 +40,10 @@ public TraceKeysRepository(Context context) {
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
okHttpBuilder.networkInterceptors().add(new UserAgentInterceptor(DP3T.getUserAgent()));
+ PublicKey signaturePublicKey = SignatureUtil.getPublicKeyFromBase64OrThrow(BuildConfig.BUCKET_PUBLIC_KEY);
+ okHttpBuilder.addInterceptor(new SignatureVerificationInterceptor(signaturePublicKey));
+
+
Retrofit bucketRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpBuilder.build())
From 7da169a6480ff66d69b0c6263bd385e4cfb1603a Mon Sep 17 00:00:00 2001
From: Florian Buenzli
Date: Tue, 1 Jun 2021 22:00:07 +0200
Subject: [PATCH 140/207] Implemented Error Popup when entering Covidcode while
checked in (info tab)
---
.../admin/bag/dp3t/infotab/InfoTabFragment.kt | 30 +++++++++++++++++--
1 file changed, 27 insertions(+), 3 deletions(-)
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 fa9ff0cee..a2b01e5a6 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
@@ -6,17 +6,20 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
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.CrowdNotifierViewModel
+import ch.admin.bag.dp3t.checkin.checkinflow.CheckOutFragment
import ch.admin.bag.dp3t.databinding.FragmentInfoTabBinding
+import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.inform.InformActivity
import ch.admin.bag.dp3t.storage.SecureStorage
import ch.admin.bag.dp3t.travel.TravelFragment
import ch.admin.bag.dp3t.travel.TravelUtils
import ch.admin.bag.dp3t.util.UrlUtil
-import ch.admin.bag.dp3t.extensions.showFragment
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
import ch.admin.bag.dp3t.whattodo.WtdInfolineAccessabilityDialogFragment
import ch.admin.bag.dp3t.whattodo.WtdPositiveTestFragment
@@ -24,7 +27,6 @@ import ch.admin.bag.dp3t.whattodo.WtdSymptomsFragment
class InfoTabFragment : Fragment() {
-
companion object {
@JvmStatic
fun newInstance() = InfoTabFragment()
@@ -33,6 +35,7 @@ class InfoTabFragment : Fragment() {
private lateinit var binding: FragmentInfoTabBinding
private val secureStorage by lazy { SecureStorage.getInstance(requireContext()) }
private val tracingViewModel: TracingViewModel by activityViewModels()
+ private val crowdNotifierViewModel: CrowdNotifierViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentInfoTabBinding.inflate(inflater).apply {
@@ -54,11 +57,32 @@ class InfoTabFragment : Fragment() {
showFragment(WtdPositiveTestFragment.newInstance())
}
wtdInformButton.setOnClickListener {
- startActivity(Intent(activity, InformActivity::class.java))
+ if (crowdNotifierViewModel.isCheckedIn.value == true) {
+ showCannotEnterCovidcodeWhileCheckedInDialog()
+ } else {
+ startActivity(Intent(activity, InformActivity::class.java))
+ }
}
}
}
+ private fun showCannotEnterCovidcodeWhileCheckedInDialog() {
+ AlertDialog.Builder(requireContext(), R.style.NextStep_AlertDialogStyle)
+ .setMessage(R.string.error_cannot_enter_covidcode_while_checked_in)
+ .setPositiveButton(R.string.checkout_button_title) { dialog, id -> showCheckOutFragment() }
+ .setNegativeButton(R.string.cancel) { dialog, id -> dialog.dismiss() }
+ .create()
+ .show()
+ }
+
+ private fun showCheckOutFragment() {
+ requireActivity().supportFragmentManager.beginTransaction()
+ .setCustomAnimations(R.anim.modal_slide_enter, R.anim.modal_slide_exit, R.anim.modal_pop_enter, R.anim.modal_pop_exit)
+ .replace(R.id.main_fragment_container, CheckOutFragment.newInstance())
+ .addToBackStack(CheckOutFragment::class.java.canonicalName)
+ .commit()
+ }
+
private fun fillContentFromConfigServer() {
val textModel = secureStorage.getWhatToDoPositiveTestTexts(getString(R.string.language_key)) ?: return
binding.frameCardTest.apply {
From 7c4c3d94e98e9b49cf1510cc72630b924a88aae0 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 2 Jun 2021 12:15:38 +0200
Subject: [PATCH 141/207] Set SwissCovidLocationData version to 4
---
.../ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
index 8e350d905..c4ede0a6d 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/generateqrcode/QRCodeViewModel.kt
@@ -30,7 +30,7 @@ private const val ONE_MINUTE_IN_MILLIS = 60 * 1000L
private const val ONE_HOUR_IN_MILLIS = 60 * ONE_MINUTE_IN_MILLIS
private const val AUTOMATIC_CHECKOUT_DELAY_MS = 12 * ONE_HOUR_IN_MILLIS
private const val CHECKOUT_WARNING_DELAY_MS = 8 * ONE_HOUR_IN_MILLIS
-private const val SWISSCOVID_LOCATION_DATA_VERSION = 1
+private const val SWISSCOVID_LOCATION_DATA_VERSION = 4
private const val QR_CODE_VALIDITY_DURATION_MS = 100000 * 24 * ONE_HOUR_IN_MILLIS // 100'000 days
private val REMINDER_DELAY_OPTIONS_MS =
listOf(if (BuildConfig.IS_FLAVOR_DEV) 1 else 30, 60, 120, 240).map { it * ONE_MINUTE_IN_MILLIS } // 30, 60, 120 and 240 minutes
From c77e218465aa860a28648fee36a3b900a2a81968 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 2 Jun 2021 14:53:39 +0200
Subject: [PATCH 142/207] Fix Onboarding Don't keep activities issue
---
.../java/ch/admin/bag/dp3t/MainActivity.kt | 15 ++--
.../OnboardingActivityResultContract.kt | 32 +++++++++
.../bag/dp3t/util/BetterActivityResult.kt | 71 -------------------
3 files changed, 38 insertions(+), 80 deletions(-)
create mode 100644 app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingActivityResultContract.kt
delete mode 100644 app/src/main/java/ch/admin/bag/dp3t/util/BetterActivityResult.kt
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 0e924ca5e..5dc568a5b 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt
+++ b/app/src/main/java/ch/admin/bag/dp3t/MainActivity.kt
@@ -30,13 +30,13 @@ import ch.admin.bag.dp3t.checkin.utils.ErrorDialog
import ch.admin.bag.dp3t.checkin.utils.NotificationHelper
import ch.admin.bag.dp3t.inform.InformActivity
import ch.admin.bag.dp3t.networking.ConfigWorker.Companion.scheduleConfigWorkerIfOutdated
-import ch.admin.bag.dp3t.onboarding.OnboardingActivity
+import ch.admin.bag.dp3t.onboarding.OnboardingActivityArgs
+import ch.admin.bag.dp3t.onboarding.OnboardingActivityResultContract
import ch.admin.bag.dp3t.onboarding.OnboardingSlidePageAdapter.Companion.UPDATE_BOARDING_VERSION
import ch.admin.bag.dp3t.onboarding.OnboardingType
import ch.admin.bag.dp3t.reports.ReportsFragment
import ch.admin.bag.dp3t.reports.ReportsOverviewFragment
import ch.admin.bag.dp3t.storage.SecureStorage
-import ch.admin.bag.dp3t.util.BetterActivityResult
import ch.admin.bag.dp3t.util.NotificationUtil
import ch.admin.bag.dp3t.util.UrlUtil
import ch.admin.bag.dp3t.viewmodel.TracingViewModel
@@ -62,7 +62,9 @@ class MainActivity : FragmentActivity() {
private val tracingViewModel: TracingViewModel by viewModels()
private val crowdNotifierViewModel: CrowdNotifierViewModel by viewModels()
- private val activityLauncher = BetterActivityResult.registerActivityForResult(this)
+ private val onboardingLauncher = registerForActivityResult(OnboardingActivityResultContract()) {
+ onOnboardingFinished(it.onboardingType, it.activityResult, it.instantAppUrl)
+ }
private val autoCheckoutBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -119,12 +121,7 @@ class MainActivity : FragmentActivity() {
}
fun launchOnboarding(onboardingType: OnboardingType, instantAppQrCodeUrl: String? = null) {
- val intent = Intent(this, OnboardingActivity::class.java).apply {
- putExtra(OnboardingActivity.ARG_ONBOARDING_TYPE, onboardingType)
- }
- activityLauncher.launch(intent) { activityResult ->
- onOnboardingFinished(onboardingType, activityResult, instantAppQrCodeUrl)
- }
+ onboardingLauncher.launch(OnboardingActivityArgs(onboardingType, instantAppQrCodeUrl))
}
private fun onOnboardingFinished(onboardingType: OnboardingType, activityResult: ActivityResult, qrCodeUrl: String? = null) {
diff --git a/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingActivityResultContract.kt b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingActivityResultContract.kt
new file mode 100644
index 000000000..05e560587
--- /dev/null
+++ b/app/src/main/java/ch/admin/bag/dp3t/onboarding/OnboardingActivityResultContract.kt
@@ -0,0 +1,32 @@
+package ch.admin.bag.dp3t.onboarding
+
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContract
+
+private const val ARG_INSTANT_APP_URL = "ARG_INSTANT_APP_URL"
+
+class OnboardingActivityResultContract : ActivityResultContract() {
+
+ override fun createIntent(context: Context, input: OnboardingActivityArgs): Intent {
+ return Intent(context, OnboardingActivity::class.java).apply {
+ putExtra(OnboardingActivity.ARG_ONBOARDING_TYPE, input.onboardingType)
+ putExtra(ARG_INSTANT_APP_URL, input.instantAppUrl)
+ }
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): OnboardingActivityResult {
+ val onboardingType = intent?.getSerializableExtra(OnboardingActivity.ARG_ONBOARDING_TYPE) as OnboardingType
+ val instantAppUrl = intent.getStringExtra(ARG_INSTANT_APP_URL)
+ return OnboardingActivityResult(ActivityResult(resultCode, intent), onboardingType, instantAppUrl)
+ }
+}
+
+data class OnboardingActivityArgs(val onboardingType: OnboardingType, val instantAppUrl: String? = null)
+
+data class OnboardingActivityResult(
+ val activityResult: ActivityResult,
+ val onboardingType: OnboardingType,
+ val instantAppUrl: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/ch/admin/bag/dp3t/util/BetterActivityResult.kt b/app/src/main/java/ch/admin/bag/dp3t/util/BetterActivityResult.kt
deleted file mode 100644
index 03b16b1bc..000000000
--- a/app/src/main/java/ch/admin/bag/dp3t/util/BetterActivityResult.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package ch.admin.bag.dp3t.util
-
-import android.content.Intent
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.ActivityResultCaller
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContract
-import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
-
-
-class BetterActivityResult private constructor(
- caller : ActivityResultCaller,
- contract : ActivityResultContract,
- var onActivityResult : ((Result) -> Unit)?,
-) {
-
- private val launcher : ActivityResultLauncher =
- caller.registerForActivityResult(contract) { onActivityResult?.invoke(it) }
-
- /**
- * Launch activity, same as [ActivityResultLauncher.launch] except that it
- * allows a callback
- * executed after receiving a result from the target activity.
- */
- /**
- * Same as [.launch] with last parameter set to `null`.
- */
- @JvmOverloads
- fun launch(
- input : Input,
- onActivityResult : ((Result) -> Unit)? = this.onActivityResult,
- ) {
- this.onActivityResult = onActivityResult
- launcher.launch(input)
- }
-
- companion object {
- /**
- * Register activity result using a [ActivityResultContract] and an in-place
- * activity result callback like
- * the default approach. You can still customise callback using [.launch].
- */
- fun registerForActivityResult(
- caller : ActivityResultCaller,
- contract : ActivityResultContract,
- onActivityResult : ((Result) -> Unit)?,
- ) : BetterActivityResult {
- return BetterActivityResult(caller, contract, onActivityResult)
- }
-
- /**
- * Same as [.registerForActivityResult] except
- * the last argument is set to `null`.
- */
- fun registerForActivityResult(
- caller : ActivityResultCaller,
- contract : ActivityResultContract,
- ) : BetterActivityResult {
- return registerForActivityResult(caller, contract, null)
- }
-
- /**
- * Specialised method for launching new activities.
- */
- fun registerActivityForResult(
- caller : ActivityResultCaller,
- ) : BetterActivityResult {
- return registerForActivityResult(caller, StartActivityForResult())
- }
- }
-}
\ No newline at end of file
From 598b6775a7dfebf7cc930e3eca87bcda43cdd00e Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Wed, 2 Jun 2021 18:02:37 +0200
Subject: [PATCH 143/207] Don't sync Checkin Trace Keys during Isolation
---
.../checkin/networking/CrowdNotifierKeyLoadWorker.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
index 34767bfc1..d48d79e16 100644
--- a/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
+++ b/app/src/main/java/ch/admin/bag/dp3t/checkin/networking/CrowdNotifierKeyLoadWorker.java
@@ -13,6 +13,7 @@
import org.crowdnotifier.android.sdk.CrowdNotifier;
import org.crowdnotifier.android.sdk.model.ExposureEvent;
import org.crowdnotifier.android.sdk.model.ProblematicEventInfo;
+import org.dpppt.android.sdk.DP3T;
import ch.admin.bag.dp3t.BuildConfig;
import ch.admin.bag.dp3t.checkin.storage.DiaryStorage;
@@ -20,6 +21,7 @@
import ch.admin.bag.dp3t.util.NotificationUtil;
import static ch.admin.bag.dp3t.checkin.utils.CrowdNotifierReminderHelper.autoCheckoutIfNecessary;
+import static org.dpppt.android.sdk.InfectionStatus.INFECTED;
public class CrowdNotifierKeyLoadWorker extends Worker {
@@ -54,6 +56,11 @@ public CrowdNotifierKeyLoadWorker(@NonNull Context context, @NonNull WorkerParam
@Override
public Result doWork() {
Log.d(LOG_TAG, "Started KeyLoadWorker");
+ if (DP3T.getStatus(getApplicationContext()).getInfectionStatus() == INFECTED) {
+ Log.d(LOG_TAG, "KeyLoadWorker: Network Request not executed");
+ return Result.success();
+ }
+
List problematicEventInfos = new TraceKeysRepository(getApplicationContext()).loadTraceKeys();
if (problematicEventInfos == null) {
Log.d(LOG_TAG, "KeyLoadWorker failure");
From 95d3dcfd3a88e735f5fbd817b3d9ab0bd1337639 Mon Sep 17 00:00:00 2001
From: Johannes Gallmann
Date: Thu, 3 Jun 2021 11:18:03 +0200
Subject: [PATCH 144/207] Update Prod Master Public Key
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e002c9cab..3fe2ea086 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -101,7 +101,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
- buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"mAofNDGOKcmVJt4fo7kWruvFRee2g0irAfIdjH6EFyd6vHY_uTb6ZXOf9eFVzTUEiZLDLL-Q2w8kZkPscWyXi3X59zuqeI25qloOF-sowHo1_-HiH7Z_4COOA030mm8J"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
manifestPlaceholders = [qrCodeHostName: "qr.swisscovid.ch"]
@@ -115,7 +115,7 @@ android {
buildConfigField 'String', 'STATS_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField 'String', 'PUBLISHED_CROWDNOTIFIER_KEYS_BASE_URL', '"https://www.pt.bfs.admin.ch/"'
buildConfigField "String", "ENTRY_QR_CODE_PREFIX", '"https://qr.swisscovid.ch"'
- buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"lW5voTRVR-jgYMiWLd04hjvyyFQG7QOyBLw0D7XbASlqlg0AviQMqgjbABZk9PcCip27szrqFyv_1YtKZE8eyzt7vtN4qKfJdWrItLRzRtjb83piN3cDt_yNo7siohQV"'
+ buildConfigField "String", "QR_MASTER_PUBLIC_KEY_BASE_64", '"mAofNDGOKcmVJt4fo7kWruvFRee2g0irAfIdjH6EFyd6vHY_uTb6ZXOf9eFVzTUEiZLDLL-Q2w8kZkPscWyXi3X59zuqeI25qloOF-sowHo1_-HiH7Z_4COOA030mm8J"'
buildConfigField 'String', 'CONFIG_CERTIFICATE', "\"${project.backend_certs.prod.CONFIG_CERTIFICATE}\""
buildConfigField 'String', 'BUCKET_PUBLIC_KEY', "\"${project.backend_certs.prod.BUCKET_PUBLIC_KEY}\""
manifestPlaceholders = [qrCodeHostName: "qr.swisscovid.ch"]
From f8ff1920901f6506c2af92c0dfc868ef6a24e32a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Thu, 3 Jun 2021 13:35:46 +0200
Subject: [PATCH 145/207] add build script to build app and compare with
another apk, including also a verison to build the instant app bundle
---
app/build.gradle | 19 +---------------
build.gradle | 17 ++++++++++++++
buildAndCompare.sh | 44 +++++++++++++++++++++++++++++++++++++
instant/build.gradle | 18 +++++++++++++++
instant/proguard-rules.pro | 6 ++++-
instant/testKeystore | Bin 0 -> 2226 bytes
6 files changed, 85 insertions(+), 19 deletions(-)
create mode 100755 buildAndCompare.sh
create mode 100644 instant/testKeystore
diff --git a/app/build.gradle b/app/build.gradle
index 3fe2ea086..25ae7a27f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,23 +17,6 @@ plugins {
apply from: 'backend_certs.gradle'
-ext.readProperty = { paramName -> readPropertyWithDefault(paramName, null) }
-ext.readPropertyWithDefault = { paramName, defaultValue ->
- if (project.hasProperty(paramName)) {
- return project.getProperties().get(paramName)
- } else {
- Properties properties = new Properties()
- if (project.rootProject.file('local.properties').exists()) {
- properties.load(project.rootProject.file('local.properties').newDataInputStream())
- }
- if (properties.getProperty(paramName) != null) {
- return properties.getProperty(paramName)
- } else {
- return defaultValue
- }
- }
-}
-
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
@@ -126,7 +109,7 @@ android {
release {
storeFile file(readPropertyWithDefault('keystoreFile', 'testKeystore'))
storePassword readProperty('keystorePassword')
- keyAlias 'keyAlias'
+ keyAlias readPropertyWithDefault('keyAlias', 'keyAlias')
keyPassword readProperty('keyAliasPassword')
}
}
diff --git a/build.gradle b/build.gradle
index 29b62a76a..27a9521ab 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,6 +13,23 @@
buildscript {
ext {
kotlinVersion = '1.4.31'
+
+ readProperty = { paramName -> readPropertyWithDefault(paramName, null) }
+ readPropertyWithDefault = { paramName, defaultValue ->
+ if (project.hasProperty(paramName)) {
+ return project.getProperties().get(paramName)
+ } else {
+ Properties properties = new Properties()
+ if (project.rootProject.file('local.properties').exists()) {
+ properties.load(project.rootProject.file('local.properties').newDataInputStream())
+ }
+ if (properties.getProperty(paramName) != null) {
+ return properties.getProperty(paramName)
+ } else {
+ return defaultValue
+ }
+ }
+ }
}
repositories {
google()
diff --git a/buildAndCompare.sh b/buildAndCompare.sh
new file mode 100755
index 000000000..f4baf2104
--- /dev/null
+++ b/buildAndCompare.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+set -e
+
+echo "Which app would you like to build (type 'app' or 'instant')?"
+read appName
+
+echo Please enter KeystoreFile name:
+read keystoreFile
+
+echo Please enter Keystore Password:
+read -s keystorePassword
+
+echo Please enter KeyAlias:
+read keyAlias
+
+echo Please enter KeyAlias Password:
+read -s keyAliasPassword
+
+echo Please enter Build Timestamp:
+read buildTimestamp
+
+#make sure we have a full clean build
+rm -rf $appName/build
+rm -rf .gradle
+
+docker build -t swisscovid-builder .
+currentPath=`pwd`
+
+if [[ $appName -eq 'app' ]] ; then
+ command='assembleProdRelease'
+else
+ command='bundleProdRelease'
+fi
+docker run --rm -v $currentPath:/home/swisscovid -w /home/swisscovid swisscovid-builder gradle $appName:assembleProdRelease -PkeystorePassword=$keystorePassword -PkeyAlias=$keyAlias -PkeyAliasPassword=$keyAliasPassword -PkeystoreFile=$keystoreFile -PbuildTimestamp=$buildTimestamp
+
+
+if [[ $appName -eq 'app' ]] ; then
+ cp $appName/build/outputs/apk/prod/release/$appName-prod-release.apk $appName-built.apk
+
+ if [[ $# -eq 1 ]] ; then
+ echo Comparing the built APK with the reference:
+ python apkdiff.py $appName-built.apk $1
+ fi
+fi
\ No newline at end of file
diff --git a/instant/build.gradle b/instant/build.gradle
index b7abc581d..def7bd2b8 100644
--- a/instant/build.gradle
+++ b/instant/build.gradle
@@ -54,6 +54,24 @@ android {
viewBinding true
}
+ signingConfigs {
+ release {
+ storeFile file(readPropertyWithDefault('keystoreFile', 'testKeystore'))
+ storePassword readProperty('keystorePassword')
+ keyAlias readPropertyWithDefault('keyAlias', 'keyAlias')
+ keyPassword readProperty('keyAliasPassword')
+ }
+ }
+
+ buildTypes {
+ release {
+ shrinkResources false
+ setMinifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.release
+ }
+ }
+
}
dependencies {
diff --git a/instant/proguard-rules.pro b/instant/proguard-rules.pro
index 481bb4348..0c4a60b13 100644
--- a/instant/proguard-rules.pro
+++ b/instant/proguard-rules.pro
@@ -18,4 +18,8 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-renamesourcefileattribute SourceFile
+
+-dontobfuscate
+
+-keep class com.sun.jna.** { *; }
\ No newline at end of file
diff --git a/instant/testKeystore b/instant/testKeystore
new file mode 100644
index 0000000000000000000000000000000000000000..d11949b7a8789b8dc792e736711b48b378ebba29
GIT binary patch
literal 2226
zcmcgt`8N~{7oNq;Fk>4dV+mt;Wp6AaYY15e{qQiXv^-U{f^%;>$^x%d*qk@hO@8Kyy9@8c2vFLy4vkn$Gd3&J~xx43OEavSO6
zsqiqH&ptXBYdu+pQURl)4l>E`^6!)W^i_V_pScJi^BAH3Fe;H9&w__MNqNn;SkvkA
zx~K5=u+wyBTASR9)a46KSPQlM_Q$~k3w{&RiZYq~A3PsKh%0>N61nqIxtlmDLYP~i
ztW2G6=8D17K9ePtnyTN0&dVB%bW2w$+#lWOd^->-FHrnmdacG}a`1ff@galB{-s3l
z2j*6TR*P3+_=+v%BupK!!F9(tJ_KL%g!NrKf>p-cQN@S5skzzlgb*;R1YNktM^2afrWH+?f6vL3)Q>7^sUmU
zyid3|r_vE7ZX7J`Jj`ZWK>VmMDX^QD*4T;mQ4H}YJu*Ot@H
zah#bUrE)h$5b{c(z5)2eCUK+{4nJe+b^ezp!n8b@a6C<{Pip5kt$lW6tDT=XOQ|l}
zcd8Sd(u`bOp<3PzOCv0SbHL3Ll?Ou|^5f@c&pK4S+7|1b+UTNMeV_H}Hu4KpzVVR|
z{8U*xMdTR7uP_iA_2iLtGwnkAzB$jFZ4A*jT2%ZUuawKQSO#PL=$BSoY1_ac#8pR^
zZEmeYiEH^05p~Y2XNPME+W%G~=rU`06A4!e-m4z$zy7?wLZ;o&GHB`ufYWXdoUA?D
zc!nEOS!nXfQXyPLYbSWj(oXGpad+7oO=9<=aY>W($NYo!;%_RcRKiANQ}nI&8ra4p
zRpCQO$8Pr}(a$xxFydXBUEqS0!Cbh5*<&UnD?XK)MJ=A`&yGlOukFj!D~XUz6Hm>)
zcD7r3aWeMf{HEFc$uN+2oPd{OMFP4#k@JxwC)%q}MzA2LaMqWk82$p7B6OXRKZi}I
zKc}OUSkN%4f5=k>SfTPkXnFCm%_s?4+n}l@?NUq5Q|f5UzBrcex0>qTo
z`K`WNggj>vwBYC+vz}3i>GI92MhP^cnbOl`Y#x@9bt6&mILI=3)@k_7QTDD@OSRiP
zn=3X?b%&BLm2OVg?Kt7vWbq_-F)mGhD1z854`+r#e&*cKTHd&}*Z*9=1Kxpj?D1BC
zXuDxK1}BnUV3C@8noZvrEl8=nTkNQKnBS-&bcGc138_crGlprK7nH6Z!c{P9FPcny
zpSEj?pZ%VP7=fUS*kMV?_(OG7xsV%+N>dDZv}h*qcU!e2E%n1>lf6}%ehP*u0&0Al
zM-32pWjHWri&-q&U&dA@>~m^3aBG&M@=$Yyw@--Qgg9XRP;T2m@4vheVyA$|sRICD
zFI+6x4HpX{6oWxP5QxQzWHgE6fU?OX8Ydob0D%Yy0OzWRz_CHWS6QGakiM}Z_o;uL
z0#qO4
zfb5PFL31eKaCn>=P93kLgttd?sNheb(tqLqrH5D`_MfAleg<$XkPCo|1;RnGKp>#K
z=6OsF(dU337G@FxMiYhizFpZE({aZ3&RhJ8uqCL`xLN)q3?Y|VqO}OQyf-@P;(UC6
zdq6(YK~7q%X|X(=w)D0%O~wvJI1Z<3f#_eg*
z&K@>8VVcU@q6MwLe?PRQ%_PWhS06`4t|YJj+}xJV4s3CGIiCh7mj9Hdf!z|jlO@e&
zxhMhJF{jtqdLW{We*$tnHPU>Fu9l0uqnBJ(l?>(M6`^a|$?Uu@8X}#^{Z`e8U(|Ym
z<}cVEpm*x}@Klw(EKjPsR}XKl-f`uru<6XDLG94N)<%oeZjwLn@G-ItBs*0l+db
zoG4ECbh*NuEPO1yoVcH@d3l+%i)d6e?L$W9!`)Yg{}l1`eojRM0z~NjvR3G?V=MO9
z-|oVs%^&FYEz2LvS5f%%+lJ-fJ!QA15vuha+LEo@KRz(_3enM{#U~y+xw3);j)-;-
zXK_JcS3-V&wIkJM!~q|NJkUrY&U-3>;=1m{Uv25Z{W8qu#yrz8byMC=j{0*8UTa~J
zfh2lfVaxhOHn=e~1R}fAa@C7uGtnB3S=TfL%K}&<{1|-KJabnm4;4kG-$~e35o4G(
z!};RdMJ)?1;yIjV6$f9+Pu`!y%0E5(MDIvCj5BO9QW5h^A%ShLpR>OQ?bxa53>`i}
zHK*UV&{`Eo_1J|X*tQNORiwlV-@GxFroJkX*h!dWym9o3Xz?jp_T87uFah_@2ZywU
JG#dpO{2RIr&({C|
literal 0
HcmV?d00001
From 2832916fd1e06980673f34e9477d9a5c9612b8c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Thu, 3 Jun 2021 13:52:02 +0200
Subject: [PATCH 146/207] update build tools version in Dockerfile and optimize
build script
---
Dockerfile | 4 ++--
app/build.gradle | 1 -
buildAndCompare.sh | 6 ++++--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 777679f88..803636bd0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,9 @@
FROM gradle:6.6.1-jdk8
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
-ENV ANDROID_BUILD_TOOLS_VERSION 29.0.3
+ENV ANDROID_BUILD_TOOLS_VERSION 30.0.1
ENV ANDROID_HOME /usr/local/android-sdk-linux
-ENV ANDROID_VERSION 29
+ENV ANDROID_VERSION 30
ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
RUN mkdir "$ANDROID_HOME" .android && \
diff --git a/app/build.gradle b/app/build.gradle
index 25ae7a27f..51f29e688 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,7 +19,6 @@ apply from: 'backend_certs.gradle'
android {
compileSdkVersion 30
- buildToolsVersion "29.0.3"
defaultConfig {
applicationId "ch.admin.bag.dp3t"
diff --git a/buildAndCompare.sh b/buildAndCompare.sh
index f4baf2104..0d67ea335 100755
--- a/buildAndCompare.sh
+++ b/buildAndCompare.sh
@@ -16,8 +16,10 @@ read keyAlias
echo Please enter KeyAlias Password:
read -s keyAliasPassword
-echo Please enter Build Timestamp:
-read buildTimestamp
+if [[ $appName -eq 'app' ]] ; then
+ echo Please enter Build Timestamp:
+ read buildTimestamp
+fi
#make sure we have a full clean build
rm -rf $appName/build
From 96baa7b46fe53fd7b55656a35e84c22953badaec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Thu, 3 Jun 2021 13:58:57 +0200
Subject: [PATCH 147/207] fix build script
---
buildAndCompare.sh | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/buildAndCompare.sh b/buildAndCompare.sh
index 0d67ea335..27be1ed96 100755
--- a/buildAndCompare.sh
+++ b/buildAndCompare.sh
@@ -16,7 +16,7 @@ read keyAlias
echo Please enter KeyAlias Password:
read -s keyAliasPassword
-if [[ $appName -eq 'app' ]] ; then
+if [ "$appName" = "app" ] ; then
echo Please enter Build Timestamp:
read buildTimestamp
fi
@@ -28,15 +28,17 @@ rm -rf .gradle
docker build -t swisscovid-builder .
currentPath=`pwd`
-if [[ $appName -eq 'app' ]] ; then
+if [ "$appName" = "app" ] ; then
command='assembleProdRelease'
+ echo building an apk
else
command='bundleProdRelease'
+ echo building an app bundle
fi
docker run --rm -v $currentPath:/home/swisscovid -w /home/swisscovid swisscovid-builder gradle $appName:assembleProdRelease -PkeystorePassword=$keystorePassword -PkeyAlias=$keyAlias -PkeyAliasPassword=$keyAliasPassword -PkeystoreFile=$keystoreFile -PbuildTimestamp=$buildTimestamp
-if [[ $appName -eq 'app' ]] ; then
+if [ "$appName" = "app" ] ; then
cp $appName/build/outputs/apk/prod/release/$appName-prod-release.apk $appName-built.apk
if [[ $# -eq 1 ]] ; then
From 0bf26c328d201260304b722f62e3aed648f9e5a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
Date: Thu, 3 Jun 2021 14:10:19 +0200
Subject: [PATCH 148/207] fix build script
---
buildAndCompare.sh | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/buildAndCompare.sh b/buildAndCompare.sh
index 27be1ed96..1a24b9020 100755
--- a/buildAndCompare.sh
+++ b/buildAndCompare.sh
@@ -30,12 +30,10 @@ currentPath=`pwd`
if [ "$appName" = "app" ] ; then
command='assembleProdRelease'
- echo building an apk
else
command='bundleProdRelease'
- echo building an app bundle
fi
-docker run --rm -v $currentPath:/home/swisscovid -w /home/swisscovid swisscovid-builder gradle $appName:assembleProdRelease -PkeystorePassword=$keystorePassword -PkeyAlias=$keyAlias -PkeyAliasPassword=$keyAliasPassword -PkeystoreFile=$keystoreFile -PbuildTimestamp=$buildTimestamp
+docker run --rm -v $currentPath:/home/swisscovid -w /home/swisscovid swisscovid-builder gradle $appName:$command -PkeystorePassword=$keystorePassword -PkeyAlias=$keyAlias -PkeyAliasPassword=$keyAliasPassword -PkeystoreFile=$keystoreFile -PbuildTimestamp=$buildTimestamp
if [ "$appName" = "app" ] ; then
From afd5cd0cd8b38f6de1a006087cbbd876c0d563db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20R=C3=B6sch?=
<30999902+simonroesch@users.noreply.github.com>
Date: Wed, 9 Jun 2021 08:11:08 +0200
Subject: [PATCH 149/207] Update README.md
---
README.md | 44 ++++++++++++++++++--------------------------
1 file changed, 18 insertions(+), 26 deletions(-)
diff --git a/README.md b/README.md
index 3b6ddec41..44e15bf86 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,15 @@
-# SwissCovid: DP3T Android App for Switzerland
+# SwissCovid Android App
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://github.com/DP-3T/dp3t-app-android-ch/blob/master/LICENSE)
![Android Build](https://github.com/DP-3T/dp3t-app-android-ch/workflows/Android%20Build/badge.svg)
+SwissCovid is the official contact tracing app of Switzerland. The SwissCovid 2.0 app uses two types of contact tracing to prevent the spread of COVID-19. The app can be installed from the [Google Play Store](https://play.google.com/store/apps/details?id=ch.admin.bag.dp3t).
-## DP3T
-The Decentralised Privacy-Preserving Proximity Tracing (DP-3T) project is an open protocol for COVID-19 proximity tracing using Bluetooth Low Energy functionality on mobile devices that ensures personal data and computation stays entirely on an individual's phone. It was produced by a core team of over 25 scientists and academic researchers from across Europe. It has also been scrutinized and improved by the wider community.
+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.
-DP-3T is a free-standing effort started at EPFL and ETHZ that produced this protocol and that is implementing it in an open-sourced app and server.
+With presence tracing people that are at the same venue at the same time are detected. For this the [CrowdNotifier Android SDK](https://github.com/CrowdNotifier/crowdnotifier-sdk-android) is used that provides a secure, decentralized, privacy-preserving presence tracing system. This feature is called SwissCovid Check-in.
-
-## Introduction
-This is a COVID-19 tracing client using the [DP3T Android SDK](https://github.com/DP-3T/dp3t-sdk-android). It is based on the previously released demo app, but uses the newest version of the SDK. This project will be released as the official COVID-19 tracing solution for Switzerland, therefore UX, messages and flows are optimized for this specific case. Nevertheless, the source code should be a solid foundation to build a similar app for other countries and demostrate how the SDK can be used in a real app.
-The app design, UX and implementation was done by [Ubique](https://www.ubique.ch?app=github).
-
In dieser Datenschutzerklärung erläutert das Bundesamt für Gesundheit (BAG), inwieweit es hinsichtlich der Nutzung der Applikation «SwissCovid-App» (nachfolgend App) in der Schweiz Personendaten bearbeitet. Das ist keine abschliessende Beschreibung; allenfalls regeln andere Datenschutzerklärungen, ähnliche Dokumente, Nutzungsbedingungen oder Anwendungsprogramme spezifische Sachverhalte.
- Das Datenschutzrecht regelt die Bearbeitung von Personendaten. Die Bundesgesetzgebung über den Datenschutz ist auf die Datenbearbeitung anwendbar. Die Datenschutzerklärung steht zudem im Einklang mit dem Epidemiengesetz vom 28. September 2012 (EpG; SR 818.101) und der Verordnung vom 24. Juni 2020 über das Proximity-Tracing-System für das Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25).
+ Das Datenschutzrecht regelt die Bearbeitung von Personendaten. Die Bundesgesetzgebung über den Datenschutz ist auf die Bearbeitung von Personendaten anwendbar. Die Datenschutzerklärung steht zudem im Einklang mit dem Epidemiengesetz vom 28. September 2012 (EpG; SR 818.101), der 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 über ein Warnsystem zu Covid-19 für Veranstaltungen vom 1. Juli 2021.
Unter Personendaten werden alle Angaben verstanden, die sich auf eine bestimmte oder bestimmbare Person beziehen. Bearbeiten meint jeden Umgang mit Personendaten, unabhängig von den angewandten Mitteln und Verfahren, insbesondere das Beschaffen, Aufbewahren, Verwenden, Umarbeiten, Bekanntgeben, Archivieren oder Vernichten der Daten.
@@ -48,23 +49,27 @@
Erhebung und Bearbeitung von Personendaten
- Das gesamte App-System ist darauf ausgerichtet, dass der Benutzer und die Benutzerin der App nicht bestimmbar sind. Die Bearbeitung von Personendaten wird minimiert. So sind keinerlei technische Rückschlüsse auf Personen, Standorte oder Geräte möglich. Ortsangaben werden keine erfasst, sondern lediglich verschlüsselte Daten betreffend die Kontakt-Ereignisse. Diese werden technisch vor Missbrauch geschützt. Das BAG kann keine Rückschlüsse auf die Benutzerinnen und Benutzer der App ziehen. Die App schützt die Daten der Benutzerinnen und Benutzer so, dass über weite Strecken kein Personenbezug hergestellt werden kann. Dennoch ist ein Personenbezug nicht absolut auszuschliessen. So besteht eine gewisse Wahrscheinlichkeit, dass bei der Benachrichtigung
+ Das gesamte App-System ist darauf ausgerichtet, dass der Benutzer und die Benutzerin der App nicht bestimmbar sind. Die Bearbeitung von Personendaten wird minimiert. So sind keinerlei technische Rückschlüsse auf Personen, Standorte oder Geräte möglich. Ortsangaben werden bei den Begegnungen via Proximity-Tracing keine erfasst, sondern lediglich verschlüsselte Daten betreffend die Kontakt-Ereignisse. Beim Warnsystem werden nur verschlüsselte Daten über Veranstaltungen und über Ansteckungsgefahren an diesen Veranstaltungen erfasst. Diese Daten werden technisch vor Missbrauch geschützt. Das BAG kann keine Rückschlüsse auf die Benutzerinnen und Benutzer der App ziehen und die Daten nicht entschlüsseln. Die App schützt die Daten der Benutzerinnen und Benutzer so, dass über weite Strecken kein Personenbezug hergestellt werden kann. Dennoch ist ein Personenbezug nicht absolut auszuschliessen. So besteht insb. beim Proximity-Tracing-System eine gewisse Wahrscheinlichkeit, dass bei der Benachrichtigung
- einer möglicherweise gefährdeten Person, diese aufgrund ihrer Erinnerung an ihre Sozialkontakte der letzten Tage allenfalls Rückschlüsse auf die Identität der infizierten Person ziehen kann. Durch die Benutzung der App können somit potenziell auch Personen identifiziert werden. Die Benachrichtigung umfasst weiter Verhaltensempfehlungen des BAG, die Information, dass die Benutzerin oder der Benutzer potenziell dem Coronavirus ausgesetzt war, die Angabe, an welchen Tagen dies der Fall war sowie den Hinweis, dass das BAG einen Leitfaden (Web-Formular) und eine Infoline zur kostenlosen Beratung betreibt. Verlässt die Benutzerin oder der Benutzer die App, um den Leitfaden auszufüllen, werden die in der App genannten Tage, an welchen die Möglichkeit einer Ansteckung bestand, automatisch an den Leitfaden übermittelt.
+ einer möglicherweise gefährdeten Person diese aufgrund ihrer Erinnerung an ihre Sozialkontakte der letzten Tage allenfalls Rückschlüsse auf die Identität der infizierten Person ziehen kann. Durch die Benutzung der App können somit potenziell auch Personen identifiziert werden. Die Benachrichtigung aufgrund eines engen Kontaktes mit einer positiv getesteten Nutzerin / einem positiv getesteten Nutzer umfasst weiter Verhaltensempfehlungen des BAG, die Information, dass die Benutzerin oder der Benutzer potenziell dem Coronavirus ausgesetzt war, die Angabe, an welchen Tagen dies der Fall war sowie den Hinweis, dass das BAG einen Leitfaden (Web-Formular) und eine Infoline zur kostenlosen Beratung betreibt. Verlässt die Benutzerin oder der Benutzer die App, um den Leitfaden auszufüllen, werden die in der App genannten Tage, an welchen die Möglichkeit einer Ansteckung bestand, automatisch an den Leitfaden übermittelt. Die Benachrichtigung aufgrund der Check-in Funktion umfasst die obengenannten Punkte, jedoch wird hier kein Leitfaden oder eine Infoline angeboten; der primäre Inhalt der Benachrichtigung ist es, sich testen zu lassen.
- Das App-System gliedert sich in zwei Komponenten:
+ Das App-System gliedert sich in drei Komponenten:
- ein System zur Verwaltung der Annäherungsdaten, bestehend aus einer Software, die von den Benutzerinnen und Benutzern auf ihren Mobiltelefonen installiert wird, und einem Backend (VA-Backend);
+ ein System zur Verwaltung der Annäherungsdaten,
+ bestehend aus einer Software, die von den Benutzerinnen und Benutzern auf ihren Mobiltelefonen installiert wird, und einem Backend (VA-Backend);
ein System zur Verwaltung von Codes zur Freischaltung der Benachrichtigungen (Codeverwaltungssystem), bestehend aus einem webbasierten Frontend und einem Backend.
- Die beiden Backends unterstehen als zentrale Server direkt der Kontrolle des BAG und werden technisch vom Bundesamt für Informatik und Kommunikation (BIT) betrieben. Die Codeverwaltungs-Frontends laufen auf Geräten der für die Generierung des Freischaltcodes («Covidcodes») berechtigten Fachpersonen.
+ ein System zur Verwaltung der Veranstaltungen, bestehend aus einer Software, die von den Benutzerinnen und Benutzern auf ihren Mobiltelefonen installiert wird, und einem Backend (Veranstaltungs-Backend).
+
+
+ Die drei Backends unterstehen als zentrale Server direkt der Kontrolle des BAG und werden technisch vom Bundesamt für Informatik und Kommunikation (BIT) betrieben. Die Codeverwaltungs-Frontends laufen auf Geräten der für die Generierung des Freischaltcodes («Covidcodes») berechtigten Fachpersonen.
@@ -78,7 +83,10 @@
die Signalstärke;
- das Datum und die geschätzte Dauer der Annäherung.
+ das Datum und die geschätzte Dauer der Annäherung;
+
+
+ die Veranstaltungs-Identifizierungscodes mit dem jeweiligen Datum, Dauer des Aufenthaltes und Bezeichnung der Veranstaltung.
@@ -107,7 +115,21 @@
- Darüber hinaus kann das App-System mit entsprechenden ausländischen Systemen verbunden werden, wenn im betreffenden Staat ein angemessener Schutz der Persönlichkeit gewährleistet wird (durch die Gesetzgebung oder hinreichende Garantien, insbesondere durch Vertrag). Ausländische Systeme gelten dann als «entsprechend», wenn sie nach den folgenden Grundsätzen des App-Systems ausgestaltet sind:
+ Das Veranstaltungs-Backend besteht aus einer Liste mit folgenden Daten:
+
+
+
+ die Veranstaltungs-Identifizierungscodes der infizierten Teilnehmenden, die in dem Zeitraum aktuell waren, in dem eine Ansteckung von anderen Personen an einer Veranstaltung möglich war;
+
+
+ dem Datum jedes Veranstaltungs-Identifizierungscodes;
+
+
+ dem verschlüsselten relevanten Zeitraum des infizierten Teilnehmenden pro Veranstaltungs-Identifizierungscode.
+
+
+
+ Darüber hinaus kann das Proximity-Tracing-System mit entsprechenden ausländischen Systemen verbunden werden, wenn im betreffenden Staat ein angemessener Schutz der Persönlichkeit gewährleistet wird (durch die Gesetzgebung oder hinreichende Garantien, insbesondere durch Vertrag). Ausländische Systeme gelten dann als «entsprechend», wenn sie nach den folgenden Grundsätzen des App-Systems ausgestaltet sind:
@@ -132,8 +154,8 @@
Zwecke und Rechtsgrundlagen
- Das vom BAG betriebene App-System und das Verbindungssystem stützt sich auf das EpG und die VPTS.
- Die App und die mit ihr einhergehenden Datenbearbeitungen bezwecken ausschliesslich, die Benutzerinnen und Benutzer, die potenziell dem Coronavirus ausgesetzt waren, unter Wahrung des Datenschutzes zu benachrichtigen sowie Statistiken aus Daten der beiden Backends im Zusammenhang mit dem Coronavirus zu erstellen.
+ Das vom BAG betriebene App-System und das Verbindungssystem stützt sich auf das EpG, die VPTS, das Covid-19-Gesetz sowie die VWV.
+ Die App und die mit ihr einhergehenden Datenbearbeitungen bezwecken ausschliesslich, die Benutzerinnen und Benutzer, die potenziell dem Coronavirus ausgesetzt waren, unter Wahrung des Datenschutzes zu benachrichtigen sowie Statistiken aus Daten der drei Backends im Zusammenhang mit dem Coronavirus zu erstellen.
Das Verbindungssystem dient dazu, dass eine solche Benachrichtigung auch zwischen verbundenen nationalen Apps möglich ist. Damit können Benutzerinnen und Benutzer der App auch dann benachrichtigt werden, wenn sie eine Annäherung zu infizierten Benutzerinnen und Benutzern einer ausländischen App gehabt haben (z.B. zu Grenzgängern und Touristen im Inland oder Kontakten im Ausland). Umgekehrt können auch Benutzerinnen und Benutzer einer verbundenen ausländischen App benachrichtigt werden bei Annäherungen zu infizierten Benutzerinnen und Benutzern der App.
@@ -144,20 +166,20 @@
Datenweitergabe
- Die Liste mit den Daten des VA-Backends wird der App im Abrufverfahren zur Verfügung gestellt. Soweit das BAG für diesen Service Dritte im In- oder Ausland beauftragt, verpflichten sich diese vertraglich, die Vorgaben nach Artikel 60a EpG und der VPTS einzuhalten. Davon ausgenommen ist die Regelung betreffend den Quellcode nach Artikel 60
+ Die Liste mit den Daten des VA-Backends sowie des Veranstaltungs-Backends wird der App im Abrufverfahren zur Verfügung gestellt. Soweit das BAG für diesen Service Dritte im In- oder Ausland beauftragt, verpflichten sich diese vertraglich, die Vorgaben nach Artikel 60a EpG, der VPTS, Artikel 3 Absatz 7 Buchstabe a Covid-19-Gesetz und der VWV einzuhalten. Davon ausgenommen ist die Regelung betreffend den Quellcode nach Artikel 60
a
- Absatz 5 Buchstabe e EpG. Das BAG kontrolliert die Einhaltung der Vorgaben. Bei der Ausführung des Auftrags anfallende Randdaten dürfen die beauftragten Dritten nicht für eigene Zwecke nutzen. Diese Daten werden nur vom BAG bzw. vom BIT ausgewertet (vgl. Ziffer 8).
+ Absatz 5 Buchstabe e EpG und Artikel 15 VWV. Das BAG kontrolliert die Einhaltung der Vorgaben. Bei der Ausführung des Auftrags anfallende Randdaten dürfen die beauftragten Dritten nicht für eigene Zwecke nutzen. Diese Daten werden nur vom BAG bzw. vom BIT ausgewertet (vgl. Ziffer 8).
Die Liste mit den privaten Schlüsseln der infizierten Benutzerinnen und Benutzer des VA-Backends wird zudem regelmässig für eine grenzüberschreitende Benachrichtigung an das Verbindungssystem übermittelt. Sodann laden die verbundenen ausländischen Systeme (zurzeit: die deutsche Corona-Warn-App) diese privaten Schlüssel herunter und stellen sie ihren Apps zum Abruf zur Verfügung (siehe Ziffer 2).
- Das BAG stellt dem Bundesamt für Statistik (BFS) periodisch den aktuellen Bestand der in beiden Backends vorhandenen Daten in anonymisierter Form für statistische Auswertungen zur Verfügung. Die Daten des Verbindungssystems können dem BFS und der zuständigen ausländischen Stelle zu Statistikzwecken in vollständig anonymisierter Form bekanntgegeben werden. Das BIT führt im Auftrag des BAG den Betrieb der gesamten Software durch und stellt den notwendigen technischen Support-Service bereit. Das BIT hat lediglich Zugriff auf Daten, soweit dies für die beschriebenen Zwecke und die Tätigkeit der betreffenden Mitarbeiter erforderlich ist. Diese sind im Umgang mit den Daten zu Vertraulichkeit verpflichtet.
+ Das BAG stellt dem Bundesamt für Statistik (BFS) periodisch den aktuellen Bestand der in den drei Backends vorhandenen Daten in anonymisierter Form für statistische Auswertungen zur Verfügung. Die Daten des Verbindungssystems können dem BFS und der zuständigen ausländischen Stelle zu Statistikzwecken in vollständig anonymisierter Form bekanntgegeben werden. Das BIT führt im Auftrag des BAG den Betrieb der gesamten Software durch und stellt den notwendigen technischen Support-Service bereit. Das BIT hat lediglich Zugriff auf Daten, soweit dies für die beschriebenen Zwecke und die Tätigkeit der betreffenden Mitarbeiter erforderlich ist. Diese sind im Umgang mit den Daten zu Vertraulichkeit verpflichtet.
- Die App verwendet eine Schnittstelle zum Betriebssystem des Mobiltelefons der Benutzerin oder des Benutzers, welche die Bearbeitung von Daten durch Apple oder Google bedingt. Die über die Schnittstelle genutzten Funktionen der Betriebssysteme müssen die Vorgaben von Artikel 60a EpG und der VPTS erfüllen. Davon ausgenommen ist die Regelung betreffend den Quellcode nach Artikel 60
+ Die App verwendet für das Proximity-Tracing-System eine Schnittstelle zum Betriebssystem des Mobiltelefons der Benutzerin oder des Benutzers, welche die Bearbeitung von Daten durch Apple oder Google bedingt. Die über die Schnittstelle genutzten Funktionen der Betriebssysteme müssen die Vorgaben von Artikel 60a EpG und der VPTS erfüllen. Davon ausgenommen ist die Regelung betreffend den Quellcode nach Artikel 60
a
@@ -173,6 +195,9 @@
die Daten des Systems zur Verwaltung der Annäherungsdaten (sowohl auf den Mobiltelefonen als auch im VA-Backend): 14 Tage nach ihrer Erfassung;
+
+ die Daten des Systems zur Verwaltung der Veranstaltungen (sowohl auf den Mobiltelefonen als auch im Veranstaltungs-Backend): 14 Tage nach ihrer Erfassung;
+
die Daten des Codeverwaltungssystems: 24 Stunden nach ihrer Erfassung;
@@ -202,7 +227,7 @@
Weiteres
- Es werden Protokolle über Zugriffe auf das VA-Backend und das Codeverwaltungssystem zu den in den Artikeln 57
+ Es werden Protokolle über Zugriffe auf das VA-Backend, das Veranstaltungs-Backend und das Codeverwaltungssystem zu den in den Artikeln 57
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 2151227e3..5b7da35b3 100644
--- a/app/src/main/assets/disclaimer/de/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/de/terms_of_use.html
@@ -7,7 +7,6 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
1.
Geltungsbereich und Zweck
@@ -18,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) und die Verordnung vom 24. Juni 2020 über das Proximity-Tracing-System für das Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25).
+ 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 über ein Warnsystem zu Covid-19 für Veranstaltungen vom 1. Juli 2021.
1.3
@@ -34,7 +33,7 @@
2.2
- Die Nutzung der App ist nicht auf ein geografisches Gebiet beschränkt. Allerdings können Warnungen nur erfolgen, wenn die ausländische App mit der SwissCovid-App interoperabel ist. Zum gegenwärtigen Zeitpunkt ist lediglich die in Deutschland genutzte Corona-Warn-App interoperabel.
+ Die Nutzung der App ist nicht auf ein geografisches Gebiet beschränkt. Allerdings können Warnungen nur erfolgen, wenn die ausländische App mit der SwissCovid-App interoperabel ist. Zum gegenwärtigen Zeitpunkt ist lediglich die in Deutschland genutzte Corona-Warn-App mit dem Proximity-Tracing-System der SwissCovid-App interoperabel.
2.3
@@ -46,18 +45,18 @@
3.1
- Mittels der App werden Benutzerinnen und Benutzer informiert, wenn sie mit mindestens einer nachweislich infizierten Benutzerin oder einem nachweislich infizierten Benutzer der SwissCovid-App oder anderen interoperablen Apps in relevantem Kontakt waren.
+ Mittels der App werden Benutzerinnen und Benutzer informiert, wenn sie mit mindestens einer nachweislich infizierten Benutzerin oder einem nachweislich infizierten Benutzer der SwissCovid-App oder anderen interoperablen Apps in relevantem Kontakt waren. Dieser relevante Kontakt kann aufgrund des Proximity-Tracings oder aufgrund der Teilnahme an einer Veranstaltung eruiert werden.
3.2
- Voraussetzung für das Funktionieren der App ist die Aktivierung von Bluetooth.
+ Voraussetzung für das Proximity-Tracing-System der App ist die Aktivierung von Bluetooth. Für das Warnsystem braucht es hingegen kein Bluetooth.
3.3
- Die App erfüllt unter Verwendung einer Schnittstelle zum Betriebssystem des Mobiltelefons der Benutzerin oder des Benutzers folgende Funktionen:
+ Die App erfüllt – beim Proximity-Tracing-System unter Verwendung einer Schnittstelle zum Betriebssystem des Mobiltelefons der Benutzerin oder des Benutzers – folgende Funktionen:
- Das Betriebssystem generiert jeden Tag einen neuen privaten Schlüssel, der keine Rückschlüsse auf die App, das Mobiltelefon und die Benutzerinnen und Benutzer ermöglicht. Innerhalb der Reichweite von Bluetooth tauscht das Betriebssystem mit allen kompatiblen und aktiven Apps einen mindestens halbstündlich wechselnden Identifizierungscode (sog. «random ID») aus, der aus einem aktuellen privaten Schlüssel abgeleitet wird, aber nicht auf diesen Schlüssel zurückgeführt werden kann und ebenfalls keine Rückschlüsse auf die App, das Mobiltelefon und deren Benutzerinnen und Benutzer ermöglicht.
+ Proximity-Tracing-System: Das Betriebssystem generiert jeden Tag einen neuen privaten Schlüssel, der keine Rückschlüsse auf die App, das Mobiltelefon und die Benutzerinnen und Benutzer ermöglicht. Innerhalb der Reichweite von Bluetooth tauscht das Betriebssystem mit allen kompatiblen und aktiven Apps einen mindestens halbstündlich wechselnden Identifizierungscode (sog. «random ID») aus, der aus einem aktuellen privaten Schlüssel abgeleitet wird, aber nicht auf diesen Schlüssel zurückgeführt werden kann und ebenfalls keine Rückschlüsse auf die App, das Mobiltelefon und deren Benutzerinnen und Benutzer ermöglicht.
Das Betriebssystem speichert auf dem Mobiltelefon die empfangenen Identifizierungscodes, die Signalstärke, das Datum und die geschätzte Dauer der Annäherung.
@@ -65,21 +64,24 @@
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.
+
3.4
- 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 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
- oder anderen interoperablen Apps, welche in der infektiösen Zeitspanne eine Annährung gemäss Ziffer 3.3 zu der infizierten Benutzerin oder dem infizierten Benutzer hatten, werden durch die eigenen Apps benachrichtigt.
+ oder anderer interoperabler Apps, welche in der infektiösen Zeitspanne eine Annährung gemäss Ziffer 3.3 zu der infizierten Benutzerin oder dem infizierten Benutzer hatten oder gleichzeitig an derselben Veranstaltung teilgenommen haben, werden durch die eigenen Apps benachrichtigt.
- Die benachrichtigten Benutzerinnen und Benutzer erfahren, dass eine Annährung stattgefunden hat bzw. dass sie potenziell dem Coronavirus ausgesetzt waren und an welchen Tagen dies der Fall war. Sie erfahren nicht, welcher Benutzer oder welche Benutzerin infiziert ist und die Benachrichtigung ausgelöst hat. Die Benachrichtigung der SwissCovid-App umfasst weiter Verhaltensempfehlungen des BAG sowie den Hinweis, dass das BAG einen Leitfaden (Web-Formular) und eine Infoline zur kostenlosen Beratung betreibt. Verlässt man die App, um den Leitfaden auszufüllen, werden die in der App genannten Tage, an welchen eine Möglichkeit der Ansteckung bestand, automatisch an den Leitfaden übermittelt.
+ Die benachrichtigten Benutzerinnen und Benutzer erfahren, dass eine Annährung stattgefunden hat bzw. dass sie potenziell dem Coronavirus ausgesetzt waren und an welchen Tagen dies der Fall war. Sie erfahren nicht, welcher Benutzer oder welche Benutzerin infiziert ist und die Benachrichtigung ausgelöst hat. Die Benachrichtigung der SwissCovid-App aufgrund eines engen Kontaktes mit einer positiv getesteten Nutzerin / einem positiv getesteten Nutzer umfasst weiter Verhaltensempfehlungen des BAG sowie den Hinweis, dass das BAG einen Leitfaden (Web-Formular) und eine Infoline zur kostenlosen Beratung betreibt. Verlässt man die App, um den Leitfaden auszufüllen, werden die in der App genannten Tage, an welchen eine Möglichkeit der Ansteckung bestand, automatisch an den Leitfaden übermittelt. Die Benachrichtigung aufgrund des Warnsystems umfasst die obengenannten Punkte, jedoch wird hier kein Leitfaden und keine Infoline angeboten, die primäre Empfehlung ist es, sich testen zu lassen.
3.5
- Bei Eingabe des Freischaltcodes werden die Benutzerinnen und Benutzer darauf aufmerksam gemacht, mit welchen Ländern (vgl. Ziffer 2.2) die App interoperabel ist. Es wird darauf hingewiesen, dass – wenn das Feld «Einverstanden» gedrückt und der Freischaltcode in die App eingegeben wird – die privaten Schlüssel nicht nur innerhalb der SwissCovid-App geteilt werden, sondern auch mit allen anderen interoperablen Apps (zurzeit die deutsche Corona-Warn-App). Die privaten Schlüssel können nur mit allen interoperablen Apps oder gar nicht geteilt werden, was dem sogenannten «One-World-Prinzip» entspricht.
+ Bei Eingabe des Freischaltcodes werden die Benutzerinnen und Benutzer darauf aufmerksam gemacht, mit welchen Ländern (vgl. Ziffer 2.2) die App interoperabel ist. Es wird darauf hingewiesen, dass – wenn das Feld «Einverstanden» gedrückt und der Freischaltcode in die App eingegeben wird – die privaten Schlüssel des Proximity-Tracing-Systems nicht nur innerhalb der SwissCovid-App geteilt werden, sondern auch mit allen anderen interoperablen Apps (zurzeit die deutsche Corona-Warn-App). Die privaten Schlüssel können nur mit allen interoperablen Apps oder gar nicht geteilt werden, was dem sogenannten «One-World-Prinzip» entspricht.
3.6
@@ -186,7 +188,7 @@
7.3
- Spätestens beim Ausserkrafttreten der in Ziffer 1.2. aufgeführten Verordnung deaktiviert das BAG die App und fordert die Benutzerinnen und Benutzer auf, diese auf dem Mobiltelefon zu deinstallieren.
+ Spätestens beim Ausserkrafttreten der in Ziffer 1.2. aufgeführten Verordnungen deaktiviert das BAG die App und fordert die Benutzerinnen und Benutzer auf, diese auf dem Mobiltelefon zu deinstallieren.
Data Protection Statement of the Federal Office of Public Health FOPH in connection with the use of the “SwissCovid app”
- Version: 9 April 2021
+ Version: 1 July 2021
In this Data Protection Statement, the Federal Office of Public Health (FOPH) explains to what extent it will process personal data in connection with the use of the application “SwissCovid app” (hereafter app) in Switzerland. This account is not exhaustive; specific matters may be governed by other data protection statements, similar documents, terms and conditions of use, or applications.
- The processing of personal data is governed by data protection legislation. The federal legislation on data protection is applicable to the data processing. In addition, the Data Protection Statement is in line with the Epidemics Act of 28 September 2012 (EpG; SR 818.101) and the Ordinance of 24 June 2020 on the Proximity Tracing System for the Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25).
+ The processing of personal data is governed by data protection legislation. The federal legislation on data protection is applicable to the processing of personal data. In addition, the Data Protection Statement is in line with the Epidemics Act of 28 September 2012 (EpG; CC 818.101), the Ordinance of 24 June 2020 on the Proximity Tracing System for the Coronavirus SARS-CoV-2 (VPTS; CC 818.101.25),
+ the Federal Act on the Statutory Principles for Federal Council Ordinances on Combating the COVID-19 Epidemic of 25 September 2020 (COVID-19 Act; CC 818.102) and the Ordinance on a COVID-19 Warning System for Events of 1 July 2021.
“Personal data” means all information relating to an identified or identifiable person. “Processing” means any operation with personal data, irrespective of the means applied and the procedure, and in particular the collection, storage, use, revision, disclosure, archiving or destruction of data.
@@ -44,20 +44,23 @@
Collection and processing of personal data
- The entire app system is designed to ensure that the app user is not identifiable. The processing of personal data is kept to a minimum. Data cannot be traced back by technical means to persons, locations or devices. What is collected is not location data, but merely encrypted data concerning proximity (contact) events. This is protected by technical means against misuse. The FOPH cannot draw any conclusions concerning app users. The app protects users’ data in such a way that it cannot, at a distance, be connected to specific persons. Connection to a specific person cannot, however, be ruled out altogether. There is a certain likelihood that, when someone is notified of a possible exposure, their recollection of social contacts over recent days may allow them to deduce the identity of the infected individual. As a result of using the app, persons may thus potentially be identified. The notification also contains the behavioural recommendations of the FOPH, the information that the user may potentially have been exposed to the coronavirus, the dates on which this was the case and it draws attention to a guide (web form) as well as the Infoline offering free advice operated by the FOPH. If the user leaves the app in order to complete the guide, the dates named in the app on which an infection may have taken place are automatically transferred to the guide.
+ The entire app system is designed to ensure that the app user is not identifiable. The processing of personal data is kept to a minimum. Data cannot be traced back by technical means to persons, locations or devices. What is collected via proximity tracing in the event of contacts is not location data, but merely encrypted data concerning proximity (contact) events. For the warning system only encrypted data about events and threats of infection at these events are collected. This data is protected by technical means against misuse. The FOPH cannot draw any conclusions concerning app users or decrypt the data. The app protects users’ data in such a way that it cannot, at a distance, be connected to specific persons. Connection to a specific person cannot, however, be ruled out altogether. In particular with the proximity tracing system, there is a certain likelihood that, when someone is notified of a possible exposure, their recollection of social contacts over recent days may allow them to deduce the identity of the infected individual. As a result of using the app, persons may thus potentially be identified. The notification on the basis of a close contact with a user who has tested negative also contains the behavioural recommendations of the FOPH, the information that the user may potentially have been exposed to the coronavirus, the dates on which this was the case and it draws attention to a guide (web form) as well as the Infoline offering free advice operated by the FOPH. If the user leaves the app in order to complete the guide, the dates named in the app on which an infection may have taken place are automatically transferred to the guide. Notifications on the basis of the check-in function cover the above-mentioned points, although here no guide or infoline is offered; the primary content of the notification is the recommendation to have a test.
- The app system has two components:
+ The app system has three components:
a proximity data management system, comprising software installed by users on their mobile phones and a back end (PM back end);
- a system for the management of codes for the activation of notifications (code management system), comprising a web-based front end and a back end.
+ a system for the management of codes for the activation of notifications (code management system), comprising a web-based front end and a back end;
- Both of the back ends, as central servers, are under the direct control of the FOPH and are operated technically by the Federal Office of Information Technology, Systems and Telecommunication (FOITT). The code management front ends run on the devices of the experts authorised to generate the activation code (Covid code).
+ a system for the management of events, comprising software installed by users on their mobile phones and a back end (event back end).
+
+
+ The three back ends, as central servers, are under the direct control of the FOPH and are operated technically by the Federal Office of Information Technology, Systems and Telecommunication (FOITT). The code management front ends run on the devices of the experts authorised to generate the activation code (Covid code).
@@ -71,7 +74,10 @@
the signal strength;
- the date and the estimated duration of proximity.
+ the date and the estimated duration of proximity;
+
+
+ the event identification codes with the relevant date, duration of attendance and designation of the event.
@@ -100,7 +106,21 @@
- The app system can also be connected to corresponding foreign systems if adequate protection of privacy is assured in the country in question (by means of corresponding legislation or sufficient guarantees, in particular in the form of an agreement). Foreign systems are considered to be “corresponding” if they are designed according to the following principles of the app system:
+ The event back end contains a list with the following data:
+
+
+
+ the event identification codes for the infected participants that were current at the time infection by other people at an event was possible;
+
+
+ the date of each event identification code;
+
+
+ the encrypted relevant period for the infected participant for each event identification code.
+
+
+
+ The proximity tracing system can also be connected to corresponding foreign systems if adequate protection of privacy is assured in the country in question (by means of corresponding legislation or sufficient guarantees, in particular in the form of an agreement). Foreign systems are considered to be “corresponding” if they are designed according to the following principles of the app system:
@@ -125,7 +145,7 @@
Purposes and legal basis
- The app system operated by the FOPH and the connection system are based on the EpG and the VPTS. The exclusive purposes of the app and the associated data processing are, in a privacy-preserving manner, to notify users potentially exposed to the coronavirus and to produce coronavirus-related statistics using data from the two back end systems.
+ The app system operated by the FOPH and the connection system are based on the EpG, the VPTS, the COVID-19 Act and the VWV. The exclusive purposes of the app and the associated data processing are, in a privacy-preserving manner, to notify users potentially exposed to the coronavirus and to produce coronavirus-related statistics using data from the three back end systems.
The connection system serves to ensure that such a notification is also possible between connected national apps. This means that users of the app can also be notified if they have been in proximity to an infected user of a foreign app (e.g. cross-border commuters and tourists in Switzerland or contacts abroad). Conversely, users of a connected foreign app can also be notified if they have been in proximity to infected users of the app.
@@ -136,15 +156,15 @@
Data transfer
- The PM back end data list is made available to the app (or front end) in the retrieval process. Insofar as the FOPH engages third parties in Switzerland or abroad to provide this service, they undertake contractually to comply with the requirements of Article 60
+ The list of data in the PM and event back end systems is made available to the app (or front end) in the retrieval process. Insofar as the FOPH engages third parties in Switzerland or abroad to provide this service, they undertake contractually to comply with the requirements of Article 60
a
- EpG and the VPTS, with the exception of the provisions concerning the source code specified in Article 60
+ EpG, the VPTS, Article 3 paragraph 7 letter a COVID-19 Act and the VWV, with the exception of the provisions concerning the source code specified in Article 60
a
- paragraph 5 letter e EpG. The FOPH monitors compliance with the legal requirements. The third parties engaged are not permitted to use non-core data arising in the execution of this task for their own purposes. This data will only be analysed by the FOPH or the FOITT (cf. Section 8).
+ paragraph 5 letter e EpG and Article 15 VWV. The FOPH monitors compliance with the legal requirements. The third parties engaged are not permitted to use non-core data arising in the execution of this task for their own purposes. This data will only be analysed by the FOPH or the FOITT (cf. Section 8).
The list with the private keys of the infected users of the PM back end is also regularly transmitted to the connection system for a cross-border notification. The connected foreign systems (currently: the German Corona-Warn-App) then download these private keys and make them available to their apps for retrieval (see
@@ -154,10 +174,10 @@
).
- The FOPH will periodically make available to the Federal Statistical Office (FSO), in an anonymised form, the data currently held in the two back end systems, for purposes of statistical analysis. The data of the connection system can be disclosed to the FSO and the responsible foreign body for statistical purposes in a completely anonymised form. The FOITT operates the entire software on behalf of the FOPH and provides the necessary technical support service. The FOITT has access to data only insofar as this is necessary for the purposes described and the activities of the employees concerned. They are bound by confidentiality in the management of the data.
+ The FOPH will periodically make available to the Federal Statistical Office (FSO), in an anonymised form, the data currently held in the three back end systems, for purposes of statistical analysis. The data of the connection system can be disclosed to the FSO and the responsible foreign body for statistical purposes in a completely anonymised form. The FOITT operates the entire software on behalf of the FOPH and provides the necessary technical support service. The FOITT has access to data only insofar as this is necessary for the purposes described and the activities of the employees concerned. They are bound by confidentiality in the management of the data.
- The app uses an interface to the operating system of the user’s mobile phone, which entails the processing of data by Apple or Google. The operating system functions used via the interface must comply with the requirements of Article 60
+ For the proximity tracing system the app uses an interface to the operating system of the user’s mobile phone, which entails the processing of data by Apple or Google. The operating system functions used via the interface must comply with the requirements of Article 60
a
@@ -177,6 +197,9 @@
data in the proximity data management system (both on mobile phones and in the PM back end): 14 days after capture;
+
+ data in the event management system (both on mobile phones and in the event back end): 14 days after their collection;
+
data in the code management system: 24 hours after capture;
@@ -206,7 +229,7 @@
Other information
- PM back end and code management system access events are logged for the purposes specified in Articles 57
+ PM back end, event back end and code management system access events are logged for the purposes specified in Articles 57
l
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 92aa2d2cf..f5bc6e64b 100644
--- a/app/src/main/assets/disclaimer/en/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/en/terms_of_use.html
@@ -7,7 +7,6 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
1.
Scope and purpose
@@ -18,7 +17,7 @@
1.2
- The Federal Office of Public Health (FOPH) app is based on the Epidemics Act of 28 September 2012 (EpG; SR 818.101) and the Ordinance of 24 June 2020 on the Proximity Tracing System for the Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25).
+ The Federal Office of Public Health (FOPH) app is based on the Epidemics Act of 28 September 2012 (EpG; SR 818.101) and the Ordinance of 24 June 2020 on the Proximity Tracing System for the Coronavirus SARS-CoV-2 (VPTS; SR 818.101.25), the Federal Act on the Statutory Principles for Federal Council Ordinances on Combating the COVID-19 Epidemic (COVID-19 Act; CC 818.102) and the Ordinance on a COVID-19 Warning System for Events of 24 June 2021.
1.3
@@ -34,7 +33,7 @@
2.2
- The use of the app is not restricted to a geographic region. However, warnings can only be issued if the foreign app is interoperable with the SwissCovid app. At present, only the Corona-Warn-App used in Germany is interoperable.
+ The use of the app is not restricted to a geographic region. However, warnings can only be issued if the foreign app is interoperable with the SwissCovid app. At present, only the Corona-Warn-App used in Germany is interoperable with the SwissCovid app’s proximity tracing system.
2.3
@@ -46,18 +45,18 @@
3.1
- Via the app, users are informed if they have been in relevant contact with at least one user of the SwissCovid app or other interoperable apps who has confirmed to have been infected.
+ Via the app, users are informed if they have been in relevant contact with at least one user of the SwissCovid app or other interoperable apps who has confirmed to have been infected. This relevant contact can be identified on the basis of proximity tracing or participation at an event.
3.2
- The activation of Bluetooth is required for the operation of the app.
+ The activation of Bluetooth is required for the operation of the app’s proximity tracing system. Bluetooth is not required, however, for the warning system.
3.3
- Using an interface to the operating system of the user’s mobile phone, the app fulfils the following functions:
+ The app – in the case of the proximity tracing system using an interface to the operating system of the user’s mobile phone – fulfils the following functions:
- Each day, the operating system generates a new private key, which cannot be connected to the app, the mobile phone or users. Within the range of Bluetooth, the operating system exchanges with all compatible, running apps an identification code (random ID), changing at least every 30 minutes, which is derived from a current private key but cannot be traced back to this key and likewise cannot be connected to the app, the mobile phone or users.
+ Proximity tracing system: Each day, the operating system generates a new private key, which cannot be connected to the app, the mobile phone or users. Within the range of Bluetooth, the operating system exchanges with all compatible, running apps an identification code (random ID), changing at least every 30 minutes, which is derived from a current private key but cannot be traced back to this key and likewise cannot be connected to the app, the mobile phone or users.
On the mobile phone, the operating system stores the identification codes received, the signal strength, the date and the estimated duration of proximity.
@@ -65,19 +64,25 @@
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.
+
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.
+ 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 are notified by their own apps.
+ 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.
- The users thus notified learn that a proximity event has occurred, i.e. that they have potentially been exposed to the coronavirus, and on what dates this was the case. They are not told which user has been infected and has triggered the notification. The notification of the SwissCovid app also contains the behavioural recommendations of the FOPH and draws attention to a guide (web form) as well as the Infoline offering free advice operated by the FOPH. If the user leaves the app in order to complete the guide, the dates named in the app on which an infection may have taken place are automatically transferred to the guide.
+ The users thus notified learn that a proximity event has occurred, i.e. that they have potentially been exposed to the coronavirus, and on what dates this was the case. They are not told which user has been infected and has triggered the notification. The notification of the SwissCovid app on the basis of close contact with a user who has tested positive also contains the behavioural recommendations of the FOPH and draws attention to a guide (web form) as well as the Infoline offering free advice operated by the FOPH. If the user leaves the app in order to complete the guide, the dates named in the app on which an infection may have taken place are automatically transferred to the guide. Notifications on the basis of the check-in function cover the above-mentioned points, although here no guide or infoline is offered; the primary content of the notification is [the recommendation to have a test.
3.5
- Upon entering the activation code, users are made aware of with which countries (see Section 2.2) the app is interoperable. Reference is made to the fact that – if the “Accept” field is tapped and the activation code is entered in the app – the private keys will not only be shared within the SwissCovid app, but rather also with other interoperable apps (currently: the German Corona-Warn-App). The private keys can only be shared with all interoperable apps or not at all, in keeping with the “one world principle”.
+ Upon entering the activation code, users are made aware of with which countries (see Section 2.2) the app is interoperable. Reference is made to the fact that – if the “Accept” field is tapped and the activation code is entered in the app – the proximity tracing system’s private keys will not only be shared within the SwissCovid app, but rather also with other interoperable apps (currently: the German Corona-Warn-App). The private keys can only be shared with all interoperable apps or not at all, in keeping with the “one world principle”.
3.6
@@ -184,7 +189,7 @@
7.3
- At the latest when the Ordinance mentioned in Section 1.2 is no longer in force, the FOPH will deactivate the app and request users to uninstall it from their mobile phone.
+ At the latest when the ordinances mentioned in Section 1.2 is no longer in force, the FOPH will deactivate the app and request users to uninstall it from their mobile phone.
Utilisation de l’application SwissCovid: déclaration de confidentialité de l’Office fédéral de la santé publique
- Version : 9 avril 2021
+ Version : 1 juillet 2021
Dans la présente déclaration de confidentialité, l’Office fédéral de la santé publique (OFSP) explique dans quelle mesure il traite les données personnelles en lien avec l’utilisation de l’application SwissCovid (ci-après « l’application ») en suisse. Il ne s’agit pas d’un descriptif exhaustif ; certains éléments spécifiques peuvent être réglés dans des déclarations de confidentialité supplémentaires, des documents similaires, des conditions d’utilisation ou des programmes d’application.
- La législation sur la protection des données règle le traitement de données personnelles. La législation fédérale sur la protection des données s’applique au traitement des données. La déclaration de confidentialité est en outre conforme à la loi du 28 septembre 2012 sur les épidémies (LEp ; RS
+ La législation sur la protection des données règle le traitement de données personnelles. Dans ce domaine, la législation fédérale sur la protection des données s’applique. La déclaration de confidentialité est en outre conforme à la loi du 28 septembre 2012 sur les épidémies (LEp ; RS
818.101
- ) et l’ordonnance du 24 juin 2020 sur le système de traçage de proximité pour le coronavirus SARS-CoV-2 (OSTP ; RS
+ ), à l’ordonnance du 24 juin 2020 sur le système de traçage de proximité pour le coronavirus SARS-CoV-2 (OSTP ; RS
818.101.25
- ).
+ ), à la loi fédérale du 25 septembre 2020 sur les bases légales des ordonnances du Conseil fédéral visant à surmonter l’épidémie de COVID-19 (loi COVID-19 ; RS
+
+ 808.102
+
+ ) et à l’ordonnance du 24 juin 2021 sur un système d’alerte COVID-19 pour les manifestations.
On entend par données personnelles toutes les informations qui se rapportent à une personne identifiée ou identifiable. Par traitement, on entend toute opération relative à des données personnelles – quels que soient les moyens et procédés utilisés – notamment la collecte, la conservation, l’exploitation, la modification, la communication, l’archivage ou la destruction de données.
@@ -56,11 +59,15 @@
Collecte et traitement de données personnelles
- Tout le système de l’application est conçu de manière à ce que ses utilisateurs ne soient pas identifiables. Le traitement de données personnelles est réduit au minimum. Ainsi, aucune indication technique sur les personnes, les lieux ou les appareils n’est possible. Les géolocalisations ne sont pas enregistrées ; seules des données cryptées relatives aux contacts ayant eu lieu sont saisies. Ces données sont techniquement protégées de toute utilisation abusive. L’OFSP ne peut tirer aucune conclusion concernant les utilisateurs de l’application. Cette dernière protège les données des utilisateurs de telle sorte qu’aucun lien ne peut être établi avec une personne précise sur de longues distances. Un rattachement à un individu ne peut toutefois pas être totalement exclu. Il existe en effet une certaine probabilité qu’une personne alertée d’un possible risque d’infection puisse éventuellement tirer des conclusions sur l’identité de la personne infectée, en se remémorant les contacts sociaux qu’elle a eus au cours des derniers jours. Les personnes qui utilisent l’application sont donc potentiellement identifiables. La notification inclut les règles de conduite recommandées par l’OFSP, l’information selon laquelle la personne a pu être exposée au coronavirus, la mention des jours où cela s’est produit ainsi que l’indication que l’OFSP propose un guide (formulaire en ligne) et une infoline de conseil gratuit. Lorsque l’utilisateur quitte l’application pour remplir le guide, les jours d’infection potentielle indiqués dans l’application sont automatiquement transmis au guide.
+ Tout le système de l’application est conçu de manière à ce que ses utilisateurs ne soient pas identifiables. Le traitement de données personnelles est réduit au minimum. Ainsi, aucune indication technique sur les personnes, les lieux ou les appareils n’est possible. Les géolocalisations ne sont pas enregistrées lors d’un contact via traçage de proximité ; seules des données cryptées relatives aux contacts ayant eu lieu sont saisies. Le système d’alerte n’enregistre que des données cryptées concernant les manifestations et les risques d’infections qu’elles présentent. Ces données sont techniquement protégées de toute utilisation abusive. L’OFSP ne peut tirer aucune conclusion concernant les utilisateurs de l’application ni décrypter les données. Cette dernière protège les données des utilisateurs de telle sorte qu’aucun lien ne peut être établi avec une personne précise sur de longues distances. Un rattachement à un individu ne peut toutefois pas être totalement exclu, en particulier pour le système de traçage de proximité. Il existe en effet une certaine probabilité qu’une personne alertée d’un possible risque d’infection puisse éventuellement tirer des conclusions sur l’identité de la personne infectée, en se remémorant les contacts sociaux qu’elle a eus au cours des derniers jours. Les personnes qui utilisent l’application sont donc potentiellement identifiables. La notification déclenchée par un contact étroit avec un utilisateur testé positif inclut les règles de conduite recommandées par l’OFSP, l’information selon laquelle la personne a pu être exposée au coronavirus, la mention des jours où cela s’est produit ainsi que l’indication que l’OFSP propose un guide (formulaire en ligne) et une infoline de conseil gratuit. Lorsque l’utilisateur quitte l’application pour remplir le guide, les jours d’infection potentielle indiqués dans l’application sont automatiquement transmis au guide. La notification due à la fonction
+
+ Check-in
+
+ comprend les points ci-dessus, à l’exception du renvoi au guide et à l’infoline ; cette notification vise principalement à inciter la personne contactée à se faire tester.
- Le système de l’application se divise en deux composantes :
+ Le système de l’application se divise en trois composantes :
un système pour gérer les données relatives aux situations de rapprochement, comprenant un logiciel que les utilisateurs installent sur leur téléphone portable ainsi qu’un
@@ -82,10 +89,21 @@
backend web
- .
+ ;
+
+
+ un système pour gérer les manifestations, comprenant un logiciel que les utilisateurs installent sur leur téléphone portable ainsi qu’un
+
+ backend
+
+ (
+
+ backend
+
+ manifestations).
- Les deux
+ Les trois
backends
@@ -107,7 +125,10 @@
la puissance du signal ;
- la date et la durée estimée de la situation de rapprochement.
+ la date et la durée estimée de la situation de rapprochement ;
+
+
+ le code d’identification de la manifestation avec sa date, sa durée et sa description.
@@ -140,7 +161,25 @@
- En outre, le système de l’application peut être relié à d’autres systèmes étrangers correspondants si un niveau adéquat de protection de la personnalité est assuré dans l’État concerné (par la législation ou par des garanties suffisantes, notamment contractuelles). Les systèmes étrangers sont ainsi réputés « correspondants » s’ils sont configurés suivant les principes ci-après du système de l’application :
+ Le
+
+ backend
+
+ manifestations comprend une liste des données suivantes :
+
+
+
+ le code d’identification de la manifestation où les participants infectés s’étaient rendus alors qu’ils auraient pu transmettre le virus ;
+
+
+ la date de chaque code d’identification de la manifestation ;
+
+
+ la période cryptée pertinente du participant infecté par code d’identification de la manifestation.
+
+
+
+ En outre, le système de traçage de proximité peut être relié à d’autres systèmes étrangers correspondants si un niveau adéquat de protection de la personnalité est assuré dans l’État concerné (par la législation ou par des garanties suffisantes, notamment contractuelles). Les systèmes étrangers sont ainsi réputés « correspondants » s’ils sont configurés suivant les principes ci-après du système de l’application :
@@ -173,7 +212,7 @@
Buts et bases légales
- Le système d’application exploité par l’OFSP ainsi que le système de liaison se fondent sur la LEp et l’OSTP. L’application et le traitement des données entrantes par ce moyen servent exclusivement à informer les utilisateurs potentiellement exposés au coronavirus, tout en respectant la protection des données, et à établir des statistiques en lien avec le coronavirus à l’aide de données issues des deux
+ Le système d’application exploité par l’OFSP ainsi que le système de liaison se fondent sur la LEp, l’OSTP, la loi COVID-19 et l’OSAM. L’application et le traitement des données entrantes par ce moyen servent exclusivement à informer les utilisateurs potentiellement exposés au coronavirus, tout en respectant la protection des données, et à établir des statistiques en lien avec le coronavirus à l’aide de données issues des trois
backends
@@ -190,7 +229,11 @@
backend
- GR est mise à la disposition de l’application (ou
+ GR et du
+
+ backend
+
+ manifestations est mise à la disposition de l’application (ou
frontend
@@ -198,11 +241,11 @@
a
- LEp et de l’OSTP. En est exclue la réglementation concernant le code source en vertu de l’art. 60
+ LEp, de l’OSTP, de l’art. 3, al. 7, let. a, de la loi COVID-19 et de l’OSAM. En est exclue la réglementation concernant le code source en vertu de l’art. 60
a
- , al. 5, let. e, LEp. L’OFSP contrôle le respect des prescriptions. Les tiers mandatés n’ont pas le droit d’utiliser à des fins personnelles les données secondaires générées durant l’exécution du contrat. Ces données sont analysées seulement par l’OFSP ou par l’OFIT (cf. ch. 8).
+ , al. 5, let. e, LEp et de l’art. 15 OSAM. L’OFSP contrôle le respect des prescriptions. Les tiers mandatés n’ont pas le droit d’utiliser à des fins personnelles les données secondaires générées durant l’exécution du contrat. Ces données sont analysées seulement par l’OFSP ou par l’OFIT (cf. ch. 8).
La liste contenant les clés privées des utilisateurs infectés du
@@ -212,14 +255,14 @@
GR est en outre régulièrement transmise au système de liaison pour une information transfrontalière. Ensuite les systèmes étrangers reliés (actuellement l’application Corona-Warn utilisée en Allemagne) téléchargent ces clés privées et les tiennent à disposition de leurs applications pour extraction (cf. ch. 2).
- L’OFSP met régulièrement à la disposition de l’Office fédéral de la statistique (OFS) le stock actuel de données existantes dans les deux
+ L’OFSP met régulièrement à la disposition de l’Office fédéral de la statistique (OFS) le stock actuel de données existantes dans les trois
backends
sous forme anonymisée. Les données du système de liaison peuvent être communiquées sous forme anonymisée à l’OFS et au service étranger compétent à des fins de statistiques. L’OFIT gère le logiciel dans son ensemble sur mandat de l’OFSP et fournit le service de soutien technique requis. L’OFIT a uniquement accès à des données lorsque cela s’avère nécessaire pour les buts et les activités spécifiques des collaborateurs concernés. Ces derniers sont tenus à la confidentialité lorsqu’ils traitent les données.
- L’application utilise une interface reliée au système d’exploitation du téléphone portable de l’utilisateur, qui nécessite le traitement des données par Apple ou Google. Les fonctionnalités des systèmes d’exploitation utilisées par l’interface doivent remplir les prescriptions de l’art. 60
+ Pour le système de traçage de proximité, l’application utilise une interface reliée au système d’exploitation du téléphone portable de l’utilisateur, qui nécessite le traitement des données par Apple ou Google. Les fonctionnalités des systèmes d’exploitation utilisées par l’interface doivent remplir les prescriptions de l’art. 60
a
@@ -245,6 +288,13 @@
GR) : 14 jours après leur saisie ;
+
+ données du système servant à gérer les manifestations (aussi bien dans le téléphone portable que dans le
+
+ backend
+
+ manifestations) : 14 jours après leur saisie ;
+
données du système des codes : 24 heures après leur saisie ;
@@ -282,7 +332,11 @@
backend
- GR et au système de contrôle du code source sont enregistrés pour les buts fixés aux art. 57
+ GR, au
+
+ backend
+
+ manifestations et au système de contrôle du code source sont enregistrés pour les buts fixés aux art. 57
l
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 c5ce324a1..f0541b163 100644
--- a/app/src/main/assets/disclaimer/fr/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/fr/terms_of_use.html
@@ -7,7 +7,6 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
1.
Champ d’application et objet
@@ -21,11 +20,15 @@
818.101
-) et sur l’ordonnance du 24 juin 2020 sur le système de traçage de proximité pour le coronavirus SARS-CoV-2 (RS
+), l’ordonnance du 24 juin 2020 sur le système de traçage de proximité pour le coronavirus SARS-CoV-2 (RS
818.101.25
-).
+), la loi fédérale du 25 septembre 2020 sur les bases légales des ordonnances du Conseil fédéral visant à surmonter l’épidémie de COVID-19 (loi COVID-19 ; RS
+
+ 818.102
+
+) et l’ordonnance du 24 juin 2021 sur un système d’alerte COVID-19 pour les manifestations.
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.
@@ -40,7 +43,7 @@
2.2
- L’utilisation de l’application n’est pas limitée à une zone géographique. Toutefois, des alertes sont possibles uniquement si l’application étrangère est interopérable avec l’application SwissCovid. Actuellement, seule l’application Corona-Warn utilisée en Allemagne remplit cette condition.
+ L’utilisation de l’application n’est pas limitée à une zone géographique. Toutefois, des alertes sont possibles uniquement si l’application étrangère est interopérable avec l’application SwissCovid. Actuellement, seule l’application Corona-Warn utilisée en Allemagne est compatible avec le système de traçage de proximité de l’application SwissCovid.
2.3
@@ -52,18 +55,18 @@
3.1
- L’application informe les utilisateurs lorsqu’ils ont été en contact avec au moins un utilisateur de l’application SwissCovid ou d’une autre application interopérable dont l’infection a été confirmée.
+ L’application informe les utilisateurs lorsqu’ils ont été en contact avec au moins un utilisateur de l’application SwissCovid ou d’une autre application interopérable dont l’infection a été confirmée. Ce contact peut être déterminé sur la base du traçage de proximité ou de la participation à une manifestation.
3.2
- Le Bluetooth doit être activé pour que l’application fonctionne.
+ Le Bluetooth doit être activé pour que le système de traçage de proximité fonctionne. En revanche, le système d’alerte n’a pas besoin du Bluetooth.
3.3
- L’application remplit les fonctions suivantes en utilisant une interface liée au système d’exploitation du téléphone portable de l’utilisateur :
+ L’application remplit les fonctions suivantes en utilisant, pour le système de traçage de proximité, une interface liée au système d’exploitation du téléphone portable de l’utilisateur :
- Le système d’exploitation génère chaque jour une nouvelle clé privée qui ne permet pas de remonter jusqu’à l’application, le téléphone portable ou à l’utilisateur. Dans le rayon de portée du Bluetooth, le système d’exploitation échange avec toutes les applications compatibles et actives un code d’identification (ID aléatoire) qui change au moins toutes les demi-heures, qui est généré à partir de la clé privée actuelle mais qui ne permet ni d’identifier la clé, ni de remonter jusqu’à l’application, au téléphone portable ou à l’utilisateur.
+ Système de traçage de proximité : le système d’exploitation génère chaque jour une nouvelle clé privée qui ne permet pas de remonter jusqu’à l’application, le téléphone portable ou à l’utilisateur. Dans le rayon de portée du Bluetooth, le système d’exploitation échange avec toutes les applications compatibles et actives un code d’identification (ID aléatoire) qui change au moins toutes les demi-heures, qui est généré à partir de la clé privée actuelle mais qui ne permet ni d’identifier la clé, ni de remonter jusqu’à l’application, au téléphone portable ou à l’utilisateur.
Le système d’exploitation enregistre les codes d’identification reçus, la puissance du signal, la date et la durée approximative du rapprochement.
@@ -71,17 +74,27 @@
À 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.
+
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.
+ 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.
- L’application informe les autres utilisateurs de l’application SwissCovid ou d'autres applications interopérables qui se sont trouvés en rapprochement selon le ch. 3.3 avec l’utilisateur infecté durant la période potentiellement infectieuse.
+ L’application informe les autres utilisateurs de l’application SwissCovid ou d'autres applications interopérables qui se sont trouvés en rapprochement selon le ch. 3.3 avec l’utilisateur infecté durant la période potentiellement infectieuse ou qui ont participé à une manifestation au même moment.
- Les utilisateurs informés apprennent qu’un rapprochement a eu lieu ou qu’ils ont potentiellement été exposés au coronavirus ainsi que les jours où cela s’est produit. Ils n’apprennent pas quel utilisateur est infecté et a déclenché la notification. Celle-ci comprend les règles de conduite recommandées par l’OFSP, l'indication que l'OFSP propose un guide (formulaire en ligne) ainsi qu'une infoline de conseil gratuit. Lorsqu'on quitte l’application pour remplir le guide, les jours d’infection potentielle indiqués dans l’application sont automatiquement transmis au guide.
+ Les utilisateurs informés apprennent qu’un rapprochement a eu lieu ou qu’ils ont potentiellement été exposés au coronavirus ainsi que les jours où cela s’est produit. Ils n’apprennent pas quel utilisateur est infecté et a déclenché la notification. La notification due à un contact étroit avec un utilisateur testé positif comprend les règles de conduite recommandées par l’OFSP, l'indication que l'OFSP propose un guide (formulaire en ligne) ainsi qu'une infoline de conseil gratuit. Lorsqu'on quitte l’application pour remplir le guide, les jours d’infection potentielle indiqués dans l’application sont automatiquement transmis au guide. La notification due au système d’alerte comprend les points ci-dessus, à l’exception du renvoi au guide et à l’infoline ; cette notification vise principalement à recommander à la personne contactée à se faire tester.
-Lors de la saisie des codes d’autorisation, les utilisateurs sont informés des pays avec lesquels l’application est interopérable (cf. ch. 2.2). Il leur est précisé que s’ils appuient sur le champ « D’accord » et qu’ils saisissent le code d’autorisation dans l’application, les clés privées ne sont pas seulement partagées dans l’application SwissCovid mais également avec toutes les autres applications interopérables (actuellement l’application allemande Corona-Warn). Suivant le principe
+Lors de la saisie des codes d’autorisation, les utilisateurs sont informés des pays avec lesquels l’application est interopérable (cf. ch. 2.2). Il leur est précisé que s’ils appuient sur le champ « D’accord » et qu’ils saisissent le code d’autorisation dans l’application, les clés privées du système de traçage ce proximité ne sont pas seulement partagées dans l’application SwissCovid mais également avec toutes les autres applications interopérables (actuellement l’application allemande Corona-Warn). Suivant le principe
One World
@@ -192,7 +205,7 @@
7.3
- Au plus tard au moment du retrait de l’ordonnance mentionnée au ch. 1.2, l’OFSP désactive l’application et prie les utilisateurs de la désinstaller de leur téléphone portable.
+ Au plus tard au moment du retrait des ordonnances mentionnées au ch. 1.2, l’OFSP désactive l’application et prie les utilisateurs de la désinstaller de leur téléphone portable.
Informativa sulla protezione dei dati dell’Ufficio federale della sanità pubblica UFSP in relazione all’utilizzo dell’«app SwissCovid»
- Versione: 9 aprile 2021
+ Versione: 1° luglio 2021
Nella presente informativa sulla protezione dei dati l’Ufficio federale della sanità pubblica (UFSP) spiega in che misura tratterà dati personali nel quadro dell’utilizzo dell’applicazione «app SwissCovid» (di seguito app) in Svizzera. Non si tratta di una descrizione esaustiva; alcuni aspetti specifici possono essere regolamentati da altre informative sulla protezione dei dati, documenti analoghi, condizioni di utilizzo o programmi di applicazione.
- Il trattamento dei dati personali è disciplinato dal diritto sulla protezione dei dati; al trattamento dei dati si applica la legislazione federale in materia. La presente informativa è inoltre conforme alla legge del 28 settembre 2012 sulle epidemie (LEp; RS 818.101) e all’ordinanza del 24 giugno 2020 sul sistema di tracciamento della prossimità per il coronavirus SARS-CoV-2 (OSTP; RS 818.101.25).
+ Il trattamento dei dati personali è disciplinato dal diritto sulla protezione dei dati; al trattamento dei dati personali si applica la legislazione federale in materia. La presente informativa è inoltre conforme alla legge del 28 settembre 2012 sulle epidemie (LEp; RS 818.101), all’ordinanza del 24 giugno 2020 sul sistema di tracciamento della prossimità per il coronavirus SARS-CoV-2 (OSTP; RS 818.101.25), alla legge federale del 25 settembre 2020 sulle basi legali delle ordinanze del Consiglio federale volte a far fronte all’epidemia di COVID-19 (legge COVID-19; RS 818.102) nonché all’ordinanza del 24 giugno 2021 su un sistema di allerta COVID-19 per le manifestazioni.
Per dati personali s’intendono tutte le informazioni relative a una persona identificata o identificabile. Il trattamento indica qualsiasi operazione relativa a dati personali, indipendentemente dai mezzi e dalle procedure impiegati, segnatamente la raccolta, la conservazione, l’utilizzazione, la modificazione, la comunicazione, l’archiviazione o la distruzione di dati.
@@ -48,20 +47,23 @@
Raccolta e trattamento dei dati personali
- L’intero sistema dell’app è concepito in modo tale che l’utente non sia identificabile. Il trattamento dei dati personali è ridotto al minimo, per cui non è in alcun modo tecnicamente possibile risalire a persone, luoghi o dispositivi. Non vengono rilevati dati sulla posizione, ma soltanto informazioni crittografate riguardanti i contatti avvenuti, protette tecnicamente da usi impropri. L’UFSP non può risalire agli utenti dell’app, che ne protegge i dati impedendo di stabilire un collegamento con una data persona sulle lunghe distanze. Un collegamento a una data persona non può tuttavia essere completamente escluso. Esiste infatti una certa probabilità che una persona informata di essere potenzialmente a rischio possa risalire all’identità della persona contagiata ricostruendo i contatti sociali avuti nei giorni precedenti. L’utilizzo dell’app potrebbe quindi permettere l’identificazione di persone. La notifica include le raccomandazioni di comportamento dell’UFSP, l’informazione che l’utente è stato potenzialmente esposto al coronavirus, l’indicazione dei giorni in cui lo è stato, la segnalazione che l’UFSP gestisce una guida (modulo web) e una linea di consulenza telefonica gratuita. Se l’utente esce dall’app per compilare il modulo web, i giorni menzionati nell’app nei quali c’è stata la possibilità di contagio vengono trasmessi automaticamente al modulo.
+ L’intero sistema dell’app è concepito in modo tale che l’utente non sia identificabile. Il trattamento dei dati personali è ridotto al minimo, per cui non è in alcun modo tecnicamente possibile risalire a persone, luoghi o dispositivi. Non vengono registrati dati sulla posizione degli incontri rilevati tramite tracciamento di prossimità, ma soltanto informazioni criptate riguardanti i contatti avvenuti. Il sistema di allerta registra solo dati criptati sulle manifestazioni e sui rischi di contagio in occasione di tali manifestazioni. Questi dati sono protetti tecnicamente da usi impropri. L’UFSP non può risalire agli utenti dell’app né decrittare i dati, che l’app protegge impedendo di stabilire un collegamento con una data persona sulle lunghe distanze. Un collegamento a una data persona non può tuttavia essere completamente escluso. Esiste infatti una certa probabilità, in particolare con il sistema di tracciamento della prossimità, che una persona informata di essere potenzialmente a rischio possa risalire all’identità della persona contagiata ricostruendo i contatti sociali avuti nei giorni precedenti. L’utilizzo dell’app potrebbe quindi permettere l’identificazione di persone. La notifica in base a un contatto stretto con un utente risultato positivo al test include le raccomandazioni di comportamento dell’UFSP, l’informazione che l’utente è stato potenzialmente esposto al coronavirus, l’indicazione dei giorni in cui lo è stato, la segnalazione che l’UFSP gestisce una guida (modulo web) e una linea di consulenza telefonica gratuita. Se l’utente esce dall’app per compilare il modulo web, i giorni menzionati nell’app nei quali c’è stata la possibilità di contagio vengono trasmessi automaticamente al modulo. La notifica in base alla funzione di check-in include i punti precedentemente menzionati, ma non l’offerta di una guida, né di una linea di consulenza telefonica, e verte principalmente sulla raccomandazione di sottoporsi al test.
- Il sistema dell’app è costituito da due componenti:
+ Il sistema dell’app è costituito da tre componenti:
un sistema di gestione dei dati di prossimità, costituito da un software che viene installato dagli utenti sul proprio telefono cellulare e da un back end (back end GP);
- un sistema di gestione dei codici di attivazione delle informazioni (sistema di gestione dei codici), costituito da un front end e un back end basati sul web.
+ un sistema di gestione dei codici di attivazione delle informazioni (sistema di gestione dei codici), costituito da un front end e un back end basati sul web;
- Entrambi i back end, che fungono da server centrali, sono sotto il controllo diretto dell’UFSP e sono tecnicamente gestiti dall’Ufficio federale dell’informatica e della telecomunicazione (UFIT). I front end per la gestione dei codici funzionano sui dispositivi degli specialisti autorizzati a generare il codice di attivazione (codice Covid).
+ un sistema di gestione delle manifestazioni, costituito da un software che viene installato dagli utenti sul proprio telefono cellulare e da un back end (back end manifestazioni).
+
+
+ I tre back end, che fungono da server centrali, sono sotto il controllo diretto dell’UFSP e sono tecnicamente gestiti dall’Ufficio federale dell’informatica e della telecomunicazione (UFIT). I front end per la gestione dei codici funzionano sui dispositivi degli specialisti autorizzati a generare il codice di attivazione (codice Covid).
@@ -69,13 +71,16 @@
- codici di identificazione (cosiddetti ID casuali) ricevuti da altri telefoni cellulari con l’app attivata;
+ i codici di identificazione (cosiddetti ID casuali) ricevuti da altri telefoni cellulari con l’app attivata;
- potenza del segnale;
+ la potenza del segnale;
- data e durata stimata della prossimità.
+ la data e la durata stimata della prossimità;
+
+
+ i codici di identificazione delle manifestazioni con la rispettiva data, la durata della permanenza e la designazione della manifestazione.
@@ -104,7 +109,21 @@
- Inoltre, il sistema dell’app può essere collegato a sistemi esteri corrispondenti se nello Stato in questione è garantita un’adeguata protezione della personalità (grazie alla legislazione o a sufficienti garanzie, in particolare contrattuali). I sistemi esteri sono considerati «corrispondenti» se sono concepiti in base ai seguenti principi del sistema dell’app:
+ Il back end manifestazioni è costituito da un elenco con i seguenti dati:
+
+
+
+ i codici di identificazione delle manifestazioni dei partecipanti infetti rilevanti nel periodo in cui è stato possibile un contagio di altre persone durante una manifestazione;
+
+
+ la data di ogni codice di identificazione della manifestazione;
+
+
+ il periodo rilevante criptato del partecipante infetto per ogni codice di identificazione della manifestazione.
+
+
+
+ Inoltre, il sistema di tracciamento della prossimità può essere collegato a sistemi esteri corrispondenti se nello Stato in questione è garantita un’adeguata protezione della personalità (grazie alla legislazione o a sufficienti garanzie, in particolare contrattuali). I sistemi esteri sono considerati «corrispondenti» se sono concepiti in base ai seguenti principi del sistema dell’app:
@@ -129,7 +148,7 @@
Scopi e basi giuridiche
- Il sistema dell’app gestito dall’UFSP e il sistema di collegamento si fondano sulla LEp e sull’OSTP. L’app e i relativi dati trattati servono unicamente a informare, nel rispetto della protezione dei dati, gli utenti che sono stati potenzialmente esposti al coronavirus e a elaborare statistiche in relazione al coronavirus a partire dai dati dei due back end.
+ Il sistema dell’app gestito dall’UFSP e il sistema di collegamento si fondano sulla LEp, sull’OSTP, sulla legge COVID-19 e sull’OSAM. L’app e i relativi dati trattati servono unicamente a informare, nel rispetto della protezione dei dati, gli utenti che sono stati potenzialmente esposti al coronavirus e a elaborare statistiche in relazione al coronavirus a partire dai dati dei tre back end.
Il sistema di collegamento fa sì che una simile informazione sia possibile anche tra app nazionali collegate. In tal modo, gli utenti dell’app possono essere informati anche se si sono trovati in prossimità di utenti infetti di un’app estera (p. es. frontalieri e turisti in Svizzera o contatti all’estero). Viceversa, anche gli utenti di un’app estera collegata possono essere informati se si sono trovati in prossimità di utenti infetti dell’app.
@@ -140,24 +159,24 @@
Trasmissione dei dati
- L’elenco con i dati del back end GP viene messo a disposizione dell’app mediante procedura di richiamo. Laddove l’UFSP commissioni tali servizi a terzi situati in Svizzera o all’estero, questi ultimi s’impegnano contrattualmente a rispettare le prescrizioni dell’articolo 60
+ L’elenco con i dati del back end GP e del back end manifestazioni viene messo a disposizione dell’app mediante procedura di richiamo. Laddove l’UFSP commissioni tali servizi a terzi situati in Svizzera o all’estero, questi ultimi s’impegnano contrattualmente a rispettare le prescrizioni dell’articolo 60
a
- LEp e dell’OSTP; è fatta salva la disposizione sul codice sorgente di cui all’articolo 60
+ LEp, dell’OSTP, dell’articolo 3 capoverso 7 lettera a legge COVID-19 nonché dell’OSAM; è fatta salva la disposizione sul codice sorgente di cui all’articolo 60
a
- capoverso 5 lettera e LEp. L’UFSP controlla che questi terzi rispettino le prescrizioni e non utilizzino per scopi propri i metadati generati durante l’esecuzione del mandato. Questi dati sono analizzati soltanto dall’UFSP o dall’UFIT (cfr. n. 8).
+ capoverso 5 lettera e LEp e all’articolo 15 OSAM. L’UFSP controlla che questi terzi rispettino le prescrizioni e non utilizzino per scopi propri i metadati generati durante l’esecuzione del mandato. Questi dati sono analizzati soltanto dall’UFSP o dall’UFIT (cfr. n. 8).
L’elenco delle chiavi private degli utenti infetti del back end GP è inoltre trasmesso regolarmente al sistema di collegamento ai fini dell’informazione transfrontaliera. I sistemi esteri collegati (attualmente: la Corona-Warn-App tedesca) scaricano quindi queste chiavi private e le mettono a disposizione delle proprie app affinché possano richiamarle (cfr. n. 2).
- L’UFSP mette periodicamente a disposizione dell’Ufficio federale di statistica (UST) per valutazioni statistiche l’attuale raccolta di dati disponibili nei due back end in forma anonimizzata. I dati del sistema di collegamento possono essere resi noti in forma completamente anonimizzata all’UST e al competente servizio estero a fini statistici. L’UFIT gestisce su incarico dell’UFSP tutti i software e fornisce il servizio di assistenza tecnica necessario. L’UFIT ha accesso solo ai dati necessari agli scopi descritti e all’attività dei collaboratori interessati, che sono tenuti a utilizzare i dati con riservatezza.
+ L’UFSP mette periodicamente a disposizione dell’Ufficio federale di statistica (UST) per valutazioni statistiche l’attuale raccolta di dati disponibili nei tre back end in forma anonimizzata. I dati del sistema di collegamento possono essere resi noti in forma completamente anonimizzata all’UST e al competente servizio estero a fini statistici. L’UFIT gestisce su incarico dell’UFSP tutti i software e fornisce il servizio di assistenza tecnica necessario. L’UFIT ha accesso solo ai dati necessari agli scopi descritti e all’attività dei collaboratori interessati, che sono tenuti a utilizzare i dati con riservatezza.
- L’app utilizza un’interfaccia verso il sistema operativo del telefono cellulare dell’utente che elabora dati attraverso Apple o Google. Le funzioni dei sistemi operativi utilizzate tramite l’interfaccia devono adempiere le prescrizioni di cui all’articolo 60
+ Per il sistema di tracciamento della prossimità, l’app utilizza un’interfaccia verso il sistema operativo del telefono cellulare dell’utente che elabora dati attraverso Apple o Google. Le funzioni dei sistemi operativi utilizzate tramite l’interfaccia devono adempiere le prescrizioni di cui all’articolo 60
a
@@ -177,6 +196,9 @@
dati del sistema di gestione dei dati di prossimità (sia nei telefoni cellulari sia nel back end GP): 14 giorni dopo la loro registrazione;
+
+ dati del sistema di gestione delle manifestazioni (sia nei telefoni cellulari sia nel back end manifestazioni): 14 giorni dopo la loro registrazione;
+
dati del sistema di gestione dei codici: 24 ore dopo la loro registrazione;
@@ -207,7 +229,7 @@
Varie
- Vengono memorizzati i registri degli accessi al back end GP e al sistema di gestione dei codici per gli scopi previsti agli articoli 57
+ Vengono memorizzati i registri degli accessi al back end GP, al back end manifestazioni e al sistema di gestione dei codici per gli scopi previsti agli articoli 57
l
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 8223a8e96..9cd394a22 100644
--- a/app/src/main/assets/disclaimer/it/terms_of_use.html
+++ b/app/src/main/assets/disclaimer/it/terms_of_use.html
@@ -7,7 +7,6 @@
~
~ SPDX-License-Identifier: MPL-2.0
-->
-
1.
Campo di applicazione e scopo
@@ -18,7 +17,7 @@
1.2
- L’app dell’Ufficio federale della sanità pubblica (UFSP) si basa sulla legge del 28 settembre 2012 (LEp; RS 818.101) e sull’ordinanza del 24 giugno 2020 sul sistema di tracciamento della prossimità per il coronavirus SARS-CoV-2 (OSTP; RS 818.101.25).
+ L’app dell’Ufficio federale della sanità pubblica (UFSP) si basa sulla legge del 28 settembre 2012 (LEp; RS 818.101), sull’ordinanza del 24 giugno 2020 sul sistema di tracciamento della prossimità per il coronavirus SARS-CoV-2 (OSTP; RS 818.101.25), sulla legge federale del 25 settembre 2020 sulle basi legali delle ordinanze del Consiglio federale volte a far fronte all’epidemia di COVID-19 (legge COVID-19; RS 818.102) nonché sull’ordinanza del 24 giugno 2021 su un sistema di allerta COVID-19 per le manifestazioni.
1.3
@@ -34,7 +33,7 @@
2.2
- L’utilizzo dell’app non è limitato a una regione geografica. Tuttavia, le segnalazioni possono aver luogo soltanto se l’app estera è interoperabile con l’app SwissCovid. Al momento attuale lo è soltanto la Corona-Warn-App impiegata in Germania.
+ L’utilizzo dell’app non è limitato a una regione geografica. Tuttavia, le segnalazioni possono aver luogo soltanto se l’app estera è interoperabile con l’app SwissCovid. Al momento attuale soltanto la Corona-Warn-App impiegata in Germania è interoperabile con il sistema di tracciamento della prossimità dell’app SwissCovid.
2.3
@@ -46,38 +45,44 @@
3.1
- L’app informa gli utenti che sono entrati in stretto contatto con almeno un utente dell’app SwissCovid o delle altre app interoperabili risultato contagiato.
+ L’app informa gli utenti che sono entrati in stretto contatto con almeno un utente dell’app SwissCovid o delle altre app interoperabili risultato infetto. Questo contatto rilevante può essere ricostruito in base al tracciamento della prossimità o alla partecipazione a una manifestazione.
3.2
- Affinché l’app funzioni è necessario che il Bluetooth sia attivo.
+ Il sistema di tracciamento della prossimità dell’app presuppone l’attivazione del Bluetooth; per il sistema di allerta ciò non è invece necessario.
3.3
- Mediante l’utilizzazione di un’interfaccia verso il sistema operativo del telefono cellulare dell’utente, l’app adempie le seguenti funzioni.
+ Con il sistema di tracciamento della prossimità e mediante l’utilizzazione di un’interfaccia verso il sistema operativo del telefono cellulare dell’utente, l’app adempie le seguenti funzioni.
- Il sistema operativo genera ogni giorno una nuova chiave privata che non consente di risalire in alcun modo all’app, al telefono cellulare o agli utenti. Scambia un codice d’identificazione (cosiddetto “random ID”) che cambia almeno una volta ogni mezz’ora con tutte le app compatibili e attive che si trovano entro la portata di Bluetooth. Tale codice deriva da una chiave privata aggiornata, ma non è riconducibile alla stessa e di conseguenza nemmeno all’app, al telefono cellulare e al suo utente.
+ Sistema di tracciamento della prossimità: il sistema operativo genera ogni giorno una nuova chiave privata che non consente di risalire in alcun modo all’app, al telefono cellulare o agli utenti. Scambia un codice d’identificazione (cosiddetto “random ID”) che cambia almeno una volta ogni mezz’ora con tutte le app compatibili e attive che si trovano entro la portata di Bluetooth. Tale codice deriva da una chiave privata aggiornata, ma non è riconducibile alla stessa e di conseguenza nemmeno all’app, al telefono cellulare e al suo utente.
Il sistema operativo memorizza sul telefono cellulare i codici d’identificazione ricevuti, la potenza del segnale, la data e la durata stimata della prossimità.
- L’app richiama periodicamente un elenco delle chiavi private degli utenti contagiati 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 contagiato è 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.
+ 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.
3.4
- In caso di contagio accertato 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 contagiato, che può immetterlo nell’app su base volontaria. La notifica ovvero l’inserimento del codice di attivazione richiedono il consenso esplicito dell'utente contagiato.
+ 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 contagiato durante il periodo di contagiosità vengono informati dalla loro app.
+ 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.
- Viene loro comunicato che vi è stata una prossimità ovvero che sono stati potenzialmente esposti al coronavirus e le date in cui tale episodio si è verificato. Non viene comunicato loro quale utente è risultato contagiato e ha attivato la notifica dell’app SwissCovid. Quest’ultima comprende anche le raccomandazioni di comportamento dell’UFSP nonché l’informazione che l’UFSP gestisce una guida (modulo web) e una linea di consulenza telefonica gratuita. Se l’utente esce dall’app per compilare il modulo web, i giorni menzionati nell’app nei quali c’è stata la possibilità di contagio vengono trasmessi automaticamente al modulo.
+ Viene loro comunicato che vi è stata una prossimità ovvero che sono stati potenzialmente esposti al coronavirus e le date in cui tale episodio si è verificato. Non viene comunicato loro quale utente è risultato infetto e ha attivato la notifica dell’app SwissCovid. La notifica in base a un contratto stretto con un utente risultato positivo al test comprende anche le raccomandazioni di comportamento dell’UFSP nonché l’informazione che l’UFSP gestisce una guida (modulo web) e una linea di consulenza telefonica gratuita. Se l’utente esce dall’app per compilare il modulo web, i giorni menzionati nell’app nei quali c’è stata la possibilità di contagio vengono trasmessi automaticamente al modulo. La notifica in base al sistema di allerta include i punti precedentemente menzionati, ma non l’offerta di una guida, né di una linea di consulenza telefonica, e verte principalmente sulla raccomandazione di sottoporsi al test.
3.5
- Al momento dell’immissione del codice di attivazione, agli utenti viene comunicato con quali Paesi è interoperabile l’app (cfr. n. 2.2). Viene inoltre segnalato che, premendo il pulsante «Accetta» e immettendo il codice di attivazione nell’app, le chiavi private vengono condivise non soltanto all’interno dell’app SwissCovid, ma anche con tutte le app interoperabili (attualmente la Corona-Warn-App tedesca). Le chiavi private possono solo essere condivise con tutte le app interoperabili o non essere condivise affatto, secondo il «principio one world».
+ Al momento dell’immissione del codice di attivazione, agli utenti viene comunicato con quali Paesi è interoperabile l’app (cfr. n. 2.2). Viene inoltre segnalato che, premendo il pulsante «Accetta» e immettendo il codice di attivazione nell’app, le chiavi private del sistema di tracciamento della prossimità vengono condivise non soltanto all’interno dell’app SwissCovid, ma anche con tutte le app interoperabili (attualmente la Corona-Warn-App tedesca). Le chiavi private possono solo essere condivise con tutte le app interoperabili o non essere condivise affatto, secondo il «principio one world».
3.6
@@ -184,7 +189,7 @@
7.3
- Al più tardi quando l’ordinanza indicata al numero 1.2 sarà abrogata, l’UFSP disattiverà l’app e inviterà gli utenti a disinstallarla dal proprio telefono cellulare.
+ Al più tardi quando le ordinanze indicate al numero 1.2 saranno abrogate, l’UFSP disattiverà l’app e inviterà gli utenti a disinstallarla dal proprio telefono cellulare.