From 7206805f4c3f77778c977bf4805131c43d695300 Mon Sep 17 00:00:00 2001 From: marcmuschko Date: Mon, 8 Jun 2020 18:59:12 +0200 Subject: [PATCH 01/17] Small illustration fixes, settings adjustment, onboarding exception (#288) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * settings icon tracing resized, missing grey color added * fixed dark mode / light mode illustrations * fixed small issue in strings * OnBoardingTracingFragment: catch exceptions also for asyncIsEnabled() (#282) Executing `InternalExposureNotificationClient.asyncIsEnabled()` may also result in an exception of `com.google.android.gms.common.api.ApiException` if connection to GMS isn't possible. Not catching this exception results in fatal crash. ``` 2020-06-08 16:33:29.693 20904-20904/de.rki.coronawarnapp E/AndroidRuntime: FATAL EXCEPTION: main Process: de.rki.coronawarnapp, PID: 20904 com.google.android.gms.common.api.ApiException: 17: API: Nearby.EXPOSURE_NOTIFICATION_API is not available on this device. Connection failed with: ConnectionResult{statusCode=UNKNOWN_ERROR_CODE(39507), resolution=null, message=null} at com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(com.google.android.gms:play-services-base@@17.3.0:4) at com.google.android.gms.common.api.internal.ApiExceptionMapper.getException(com.google.android.gms:play-services-base@@17.3.0:2) at com.google.android.gms.common.api.internal.zah.zaa(com.google.android.gms:play-services-base@@17.3.0:18) at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base@@17.3.0:211) at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base@@17.3.0:217) at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base@@17.3.0:115) at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.onConnectionFailed(com.google.android.gms:play-services-base@@17.3.0:79) at com.google.android.gms.common.internal.zag.onConnectionFailed(com.google.android.gms:play-services-base@@17.3.0:2) at com.google.android.gms.common.internal.BaseGmsClient$zzf.zza(com.google.android.gms:play-services-basement@@17.3.0:6) at com.google.android.gms.common.internal.BaseGmsClient$zza.zza(com.google.android.gms:play-services-basement@@17.3.0:25) at com.google.android.gms.common.internal.BaseGmsClient$zzc.zzc(com.google.android.gms:play-services-basement@@17.3.0:11) at com.google.android.gms.common.internal.BaseGmsClient$zzb.handleMessage(com.google.android.gms:play-services-basement@@17.3.0:49) at android.os.Handler.dispatchMessage(Handler.java:106) at com.google.android.gms.internal.common.zzi.dispatchMessage(com.google.android.gms:play-services-basement@@17.3.0:8) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67) ``` Co-authored-by: Jakob Möller Co-authored-by: marcmuschko * build fixes in tests Co-authored-by: lenke182 Co-authored-by: Jakob Möller --- .../onboarding/OnboardingTracingFragment.kt | 16 +- .../util/formatter/FormatterSettingsHelper.kt | 8 +- ...bmission_illustration_tan_hotline_card.xml | 160 ++++++------------ .../ic_settings_tracing_active_small.xml | 28 +++ .../ic_settings_tracing_inactive_small.xml | 28 +++ ..._submission_illustration_tan_code_card.xml | 118 ++++++------- .../res/layout/fragment_main_overview.xml | 13 +- .../layout/include_information_details.xml | 2 +- .../main/res/layout/include_setting_row.xml | 1 - .../src/main/res/values-de/strings.xml | 6 +- .../src/main/res/values-en/strings.xml | 8 +- .../src/main/res/values/strings.xml | 4 +- .../src/main/res/values/styles.xml | 1 - .../http/WebRequestBuilderTest.kt | 5 +- .../transaction/RiskLevelTransactionTest.kt | 1 - .../submission/SubmissionTanViewModelTest.kt | 26 +-- ...atorTest.kt => VerificationServiceTest.kt} | 0 .../rki/coronawarnapp/util/TanHelperTest.kt | 11 +- .../util/formatter/FormatterHelperTest.kt | 30 ++-- .../formatter/FormatterSettingsHelperTest.kt | 5 - 20 files changed, 211 insertions(+), 260 deletions(-) create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_settings_tracing_active_small.xml create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_settings_tracing_inactive_small.xml rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/{VersionComparatorTest.kt => VerificationServiceTest.kt} (100%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragment.kt index 3ff6a0f5df3..a8335464d1e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragment.kt @@ -111,18 +111,18 @@ class OnboardingTracingFragment : BaseFragment(), private fun resetTracing() { // Reset tracing state in onboarding lifecycleScope.launch { - if (InternalExposureNotificationClient.asyncIsEnabled()) { - try { + try { + if (InternalExposureNotificationClient.asyncIsEnabled()) { InternalExposureNotificationClient.asyncStop() // Reset initial activation timestamp LocalData.initialTracingActivationTimestamp(0L) - } catch (exception: Exception) { - exception.report( - ExceptionCategory.EXPOSURENOTIFICATION, - OnboardingTracingFragment.TAG, - null - ) } + } catch (exception: Exception) { + exception.report( + ExceptionCategory.EXPOSURENOTIFICATION, + OnboardingTracingFragment.TAG, + null + ) } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt index 1c936b039dc..c37cd7004a1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt @@ -230,7 +230,7 @@ fun formatSettingsTracingIconColor(tracing: Boolean, bluetooth: Boolean, connect val appContext = CoronaWarnApplication.getAppContext() return when (tracingStatusHelper(tracing, bluetooth, connection)) { TracingStatusHelper.CONNECTION, TracingStatusHelper.BLUETOOTH -> - appContext.getColor(R.color.colorTextSemanticRed) + appContext.getColor(R.color.colorTextPrimary3) TracingStatusHelper.TRACING_ACTIVE -> appContext.getColor(R.color.colorAccentTintIcon) TracingStatusHelper.TRACING_INACTIVE -> @@ -257,10 +257,10 @@ fun formatSettingsTracingIcon( TracingStatusHelper.CONNECTION, TracingStatusHelper.BLUETOOTH, TracingStatusHelper.TRACING_ACTIVE -> - appContext.getDrawable(R.drawable.ic_settings_tracing_active) + appContext.getDrawable(R.drawable.ic_settings_tracing_active_small) TracingStatusHelper.TRACING_INACTIVE -> - appContext.getDrawable(R.drawable.ic_settings_tracing_inactive) - else -> appContext.getDrawable(R.drawable.ic_settings_tracing_inactive) + appContext.getDrawable(R.drawable.ic_settings_tracing_inactive_small) + else -> appContext.getDrawable(R.drawable.ic_settings_tracing_inactive_small) } } diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_submission_illustration_tan_hotline_card.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_submission_illustration_tan_hotline_card.xml index 39c3cb3badc..5109f8f9ea8 100644 --- a/Corona-Warn-App/src/main/res/drawable-night/ic_submission_illustration_tan_hotline_card.xml +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_submission_illustration_tan_hotline_card.xml @@ -1,177 +1,115 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_settings_tracing_inactive_small.xml b/Corona-Warn-App/src/main/res/drawable/ic_settings_tracing_inactive_small.xml new file mode 100644 index 00000000000..42ab5c67de4 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_settings_tracing_inactive_small.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_submission_illustration_tan_code_card.xml b/Corona-Warn-App/src/main/res/drawable/ic_submission_illustration_tan_code_card.xml index c89aac841f8..3ec0fc0081c 100644 --- a/Corona-Warn-App/src/main/res/drawable/ic_submission_illustration_tan_code_card.xml +++ b/Corona-Warn-App/src/main/res/drawable/ic_submission_illustration_tan_code_card.xml @@ -10,88 +10,66 @@ android:fillColor="#E7E7E7" android:fillType="nonZero" android:strokeColor="#00000000"/> + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + - - + app:layout_constraintTop_toBottomOf="@+id/main_overview_test"> "Häufige Fragen" "Hier finden Sie Antworten auf häufig gestellte Fragen rund um die Corona-Warn-App." - + "https://www.bundesregierung.de/corona-warn-app-faq" "+49 (0)800 7540001" - - "Tel:+49 800 7540001" + + "tel:+49 800 7540001" "Unser Kundenservice ist für Sie da." diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index 139deb40611..dc3c2d000c8 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -143,8 +143,8 @@ "FAQ" "Here you can find answers to frequently asked questions about the Corona-Warn-App." - - "https://www.bundesregierung.de/corona-warn-app-faq" + + "http://www.bundesregierung.de/corona-warn-app-faq-englisch" "+49 (0)800 7540001" - - "Phone: +49 800 7540001" + + "tel:+49 800 7540001" "Our customer service is here to help." diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index ead09a21a67..91f7ef82d90 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -224,7 +224,7 @@ Häufige Fragen Hier finden Sie Antworten auf häufig gestellte Fragen rund um die Corona-Warn-App. - + https://www.bundesregierung.de/corona-warn-app-faq +49 (0)800 7540001 - + tel:+49 800 7540001 Unser Kundenservice ist für Sie da. diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index 4039163558d..6d201e604cd 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -144,7 +144,6 @@ + + + + + + + @@ -189,8 +204,8 @@ Progress Bar ###################################### --> - + 56dp 48dp 32dp 24dp @@ -8,19 +9,19 @@ 3dp - 34sp + 34sp 24sp 18sp 16sp 12sp - 14sp 20sp 4sp - 40dp + 56dp + 40dp 10dp @@ -64,6 +65,12 @@ 8dp 0dp + + @dimen/spacing_huge + 23dp + 10 + 5 + diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 200c7cfc62a..23b3d6bf41a 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -126,6 +126,13 @@ preference_last_three_hours_from_server + + + + Zurück + From b0813b61107802bac290a81e01c0c442b3569fda Mon Sep 17 00:00:00 2001 From: ksergeevit <64887317+ksergeevit@users.noreply.github.com> Date: Tue, 9 Jun 2020 10:20:35 +0300 Subject: [PATCH 06/17] EN tests (#284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakob Möller --- Corona-Warn-App/build.gradle | 3 +- .../java/de/rki/coronawarnapp/TestFragment.kt | 20 +++ ...xposureNotificationPermissionHelperTest.kt | 158 ++++++++++++++++++ .../ExposureStateUpdateReceiverTest.kt | 56 +++++++ 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/TestFragment.kt create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationPermissionHelperTest.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/receiver/ExposureStateUpdateReceiverTest.kt diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 3ad809646f4..179b21c5758 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -181,7 +181,8 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.ext:truth:1.2.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1' - + androidTestImplementation "io.mockk:mockk-android:1.10.0" + debugImplementation 'androidx.fragment:fragment-testing:1.2.4' // Play Services implementation 'com.google.android.play:core:1.7.3' diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/TestFragment.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/TestFragment.kt new file mode 100644 index 00000000000..45170cdb4e8 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/TestFragment.kt @@ -0,0 +1,20 @@ +package de.rki.coronawarnapp + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment + +/** + * Test fragment with test view, [TextView] required for view lifecycle owner. + * + * @see [Fragment.getViewLifecycleOwner] + */ +class TestFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return TextView(this.context) + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationPermissionHelperTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationPermissionHelperTest.kt new file mode 100644 index 00000000000..ebcab98c7e8 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationPermissionHelperTest.kt @@ -0,0 +1,158 @@ +package de.rki.coronawarnapp.nearby + +import androidx.fragment.app.Fragment +import androidx.fragment.app.testing.FragmentScenario +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.common.api.Status +import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey +import de.rki.coronawarnapp.CoronaWarnApplication +import de.rki.coronawarnapp.TestFragment +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkAll +import org.hamcrest.CoreMatchers.`is` +import org.hamcrest.MatcherAssert.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * InternalExposureNotificationPermissionHelper test. + */ +@RunWith(AndroidJUnit4::class) +class InternalExposureNotificationPermissionHelperTest { + private lateinit var scenario: FragmentScenario + private var fail = false + private var startSuccess = false + private var sharingSuccess = false + private val callback = object : InternalExposureNotificationPermissionHelper.Callback { + override fun onFailure(exception: Exception?) { + fail = true + } + override fun onStartPermissionGranted() { + startSuccess = true + } + override fun onKeySharePermissionGranted(keys: List) { + sharingSuccess = true + } + } + + /** + * Launch test fragment, required for view lifecycle owner. + * + * @see [InternalExposureNotificationPermissionHelper] + * @see [Fragment.getViewLifecycleOwner] + */ + @Before + fun setUp() { + fail = false + startSuccess = false + sharingSuccess = false + mockkObject(InternalExposureNotificationClient) + scenario = launchFragmentInContainer() + } + + /** + * Test tracing permission request assuming EN Client is enabled. + */ + @Test + fun testRequestPermissionToStartTracingENIsEnabled() { + coEvery { InternalExposureNotificationClient.asyncIsEnabled() } returns true + scenario.onFragment { + val helper = InternalExposureNotificationPermissionHelper(it, callback) + helper.requestPermissionToStartTracing() + } + assertThat(fail, `is`(false)) + assertThat(startSuccess, `is`(true)) + } + + /** + * Test tracing permission request assuming EN Client is disabled. + */ + @Test + fun testRequestPermissionToStartTracingENIsNotEnabled() { + coEvery { InternalExposureNotificationClient.asyncIsEnabled() } returns false + // not every device/emulator has access to exposure notifications Google API: + coEvery { InternalExposureNotificationClient.asyncStart() } returns mockk() + + scenario.onFragment { + val helper = InternalExposureNotificationPermissionHelper(it, callback) + helper.requestPermissionToStartTracing() + } + assertThat(fail, `is`(false)) + assertThat(startSuccess, `is`(true)) + } + + /** + * Test tracing permission request exception handling. + */ + @Test + fun testRequestPermissionToStartTracingExceptionHandling() { + coEvery { InternalExposureNotificationClient.asyncIsEnabled() } returns false + + // not every device/emulator has access to exposure notifications Google API: + coEvery { InternalExposureNotificationClient.asyncStart() } throws mockApiException(Status.RESULT_CANCELED) + + scenario.onFragment { + val helper = InternalExposureNotificationPermissionHelper(it, callback) + helper.requestPermissionToStartTracing() + } + assertThat(fail, `is`(true)) + assertThat(startSuccess, `is`(false)) + } + + /** + * Test keys sharing permission request. + */ + @Test + fun testRequestPermissionToShareKeys() { + // not every device/emulator has access to exposure notifications Google API: + coEvery { InternalExposureNotificationClient.asyncGetTemporaryExposureKeyHistory() } returns mockk() + + scenario.onFragment { + val helper = InternalExposureNotificationPermissionHelper(it, callback) + helper.requestPermissionToShareKeys() + } + assertThat(fail, `is`(false)) + assertThat(sharingSuccess, `is`(true)) + } + + /** + * Test keys sharing permission request exception handling. + */ + @Test + fun testRequestPermissionToShareKeysException() { + // not every device/emulator has access to exposure notifications Google API: + coEvery { + InternalExposureNotificationClient.asyncGetTemporaryExposureKeyHistory() + } throws mockApiException(Status.RESULT_CANCELED) + + scenario.onFragment { + val helper = InternalExposureNotificationPermissionHelper(it, callback) + helper.requestPermissionToShareKeys() + } + assertThat(fail, `is`(true)) + assertThat(sharingSuccess, `is`(false)) + } + + private fun mockApiException(status: Status): ApiException { + mockkObject(LocalBroadcastManager.getInstance(CoronaWarnApplication.getAppContext())) + val exception = ApiException(status) + // don't need a dialog for exception + every { + LocalBroadcastManager.getInstance(CoronaWarnApplication.getAppContext()).sendBroadcast(any()) + } returns true + return exception + } + + @After + fun cleanUp() { + unmockkAll() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/receiver/ExposureStateUpdateReceiverTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/receiver/ExposureStateUpdateReceiverTest.kt new file mode 100644 index 00000000000..19ac4f2c87d --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/receiver/ExposureStateUpdateReceiverTest.kt @@ -0,0 +1,56 @@ +package de.rki.coronawarnapp.receiver + +import android.content.Context +import android.content.Intent +import androidx.work.WorkManager +import androidx.work.WorkRequest +import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Test + +/** + * ExposureStateUpdateReceiver test. + */ +class ExposureStateUpdateReceiverTest { + @MockK + private lateinit var context: Context + @MockK + private lateinit var intent: Intent + + @Before + fun setUp() { + MockKAnnotations.init(this) + mockkStatic(WorkManager::class) + every { intent.action } returns ExposureNotificationClient.ACTION_EXPOSURE_STATE_UPDATED + every { intent.getStringExtra(ExposureNotificationClient.EXTRA_TOKEN) } returns "token" + } + + /** + * Test ExposureStateUpdateReceiver. + */ + @Test + fun testExposureStateUpdateReceiver() { + val wm = mockk() + every { WorkManager.getInstance(context) } returns wm + every { wm.enqueue(any()) } answers { mockk() } + + ExposureStateUpdateReceiver().onReceive(context, intent) + + verify { + wm.enqueue(any()) + } + } + + @After + fun cleanUp() { + unmockkAll() + } +} From d51c9d55a338237167f088e422bf9801087aa8f2 Mon Sep 17 00:00:00 2001 From: apopovsap <66370584+apopovsap@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:32:37 +0300 Subject: [PATCH 07/17] Tests/util/formatter (#286) * add unit tests for FormatterSubmissionHelper from util/formatter * FormatterSubmissionHelperTest.kt unit tests done * Actualised with dev Co-authored-by: Hee Tatt Ooi --- .../FormatterSubmissionHelperTest.kt | 665 ++++++++++++++++++ 1 file changed, 665 insertions(+) create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt new file mode 100644 index 00000000000..6bae45fc14a --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt @@ -0,0 +1,665 @@ +package de.rki.coronawarnapp.util.formatter + +import android.content.Context +import android.graphics.drawable.Drawable +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.style.ForegroundColorSpan +import android.view.View +import de.rki.coronawarnapp.CoronaWarnApplication +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.util.DeviceUIState +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.mockkConstructor +import io.mockk.mockkObject +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import org.hamcrest.CoreMatchers.`is` +import org.hamcrest.MatcherAssert.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test + +class FormatterSubmissionHelperTest { + + @MockK + private lateinit var context: Context + + @MockK + private lateinit var drawable: Drawable + + @Before + fun setUp() { + MockKAnnotations.init(this) + mockkObject(CoronaWarnApplication.Companion) + mockkStatic(SpannableStringBuilder::class) + mockkStatic(Spannable::class) + + + every { CoronaWarnApplication.getAppContext() } returns context + + every { context.getString(R.string.test_result_card_status_positive) } returns R.string.test_result_card_status_positive.toString() + every { context.getString(R.string.test_result_card_status_negative) } returns R.string.test_result_card_status_negative.toString() + every { context.getString(R.string.test_result_card_status_invalid) } returns R.string.test_result_card_status_invalid.toString() + every { context.getString(R.string.test_result_card_virus_name_text) } returns R.string.test_result_card_virus_name_text.toString() + every { context.getString(R.string.test_result_card_status_pending) } returns R.string.test_result_card_status_pending.toString() + every { context.getString(R.string.test_result_card_status_invalid) } returns R.string.test_result_card_status_invalid.toString() + + every { context.getColor(R.color.colorTextSemanticGreen) } returns R.color.colorTextSemanticGreen + every { context.getColor(R.color.colorTextSemanticRed) } returns R.color.colorTextSemanticRed + + every { context.getDrawable(R.drawable.ic_test_result_illustration_invalid) } returns drawable + every { context.getDrawable(R.drawable.ic_test_result_illustration_pending) } returns drawable + every { context.getDrawable(R.drawable.ic_test_result_illustration_positive) } returns drawable + every { context.getDrawable(R.drawable.ic_main_illustration_negative) } returns drawable + + every { context.getString(R.string.submission_status_card_title_available) } returns R.string.submission_status_card_title_available.toString() + every { context.getString(R.string.submission_status_card_title_pending) } returns R.string.submission_status_card_title_pending.toString() + + every { context.getString(R.string.submission_status_card_body_invalid) } returns R.string.submission_status_card_body_invalid.toString() + every { context.getString(R.string.submission_status_card_body_negative) } returns R.string.submission_status_card_body_negative.toString() + every { context.getString(R.string.submission_status_card_body_positive) } returns R.string.submission_status_card_body_positive.toString() + every { context.getString(R.string.submission_status_card_body_pending) } returns R.string.submission_status_card_body_pending.toString() + + every { context.getString(R.string.submission_status_card_button_show_results) } returns R.string.submission_status_card_button_show_results.toString() + + every { context.getDrawable(R.drawable.ic_main_illustration_pending) } returns drawable + every { context.getDrawable(R.drawable.ic_main_illustration_negative) } returns drawable + every { context.getDrawable(R.drawable.ic_main_illustration_invalid) } returns drawable + every { context.getDrawable(R.drawable.ic_main_illustration_invalid) } returns drawable + + every { context.getDrawable(R.drawable.ic_test_result_illustration_negative) } returns drawable + + } + + private fun formatTestResultSpinnerVisibleBase(oUiStateState: ApiRequestState?, iResult: Int) { + val result = formatTestResultSpinnerVisible(uiStateState = oUiStateState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultVisibleBase(oUiStateState: ApiRequestState?, iResult: Int) { + val result = formatTestResultVisible(uiStateState = oUiStateState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultStatusTextBase(oUiState: DeviceUIState?, iResult: String) { + val result = formatTestResultStatusText(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultStatusColorBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatTestResultStatusColor(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestStatusIconBase(oUiState: DeviceUIState?) { + val result = formatTestStatusIcon(uiState = oUiState) + assertThat(result, `is`(drawable)) + } + + private fun formatTestResultPendingStepsVisibleBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatTestResultPendingStepsVisible(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultNegativeStepsVisibleBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatTestResultNegativeStepsVisible(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultPositiveStepsVisibleBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatTestResultPositiveStepsVisible(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultInvalidStepsVisibleBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatTestResultInvalidStepsVisible(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatSubmissionStatusCardContentTitleTextBase(oUiState: DeviceUIState?, iResult: String) { + val result = formatSubmissionStatusCardContentTitleText(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatSubmissionStatusCardContentBodyTextBase(oUiState: DeviceUIState?, iResult: String) { + val result = formatSubmissionStatusCardContentBodyText(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + + private fun formatSubmissionStatusCardContentStatusTextVisibleBase(oUiState: DeviceUIState?, iResult: Int) { + val result = formatSubmissionStatusCardContentStatusTextVisible(uiState = oUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatSubmissionStatusCardContentIconBase(oUiState: DeviceUIState?) { + val result = formatSubmissionStatusCardContentIcon(uiState = oUiState) + assertThat(result, `is`(drawable)) + } + + private fun formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered: Boolean?, + bUiStateState: ApiRequestState?, iResult: Int + ) { + val result = formatSubmissionStatusCardFetchingVisible( + deviceRegistered = bDeviceRegistered, + uiStateState = bUiStateState + ) + assertThat(result, `is`(iResult)) + } + + private fun formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState: DeviceUIState?, + iResult: Int + ) { + val result = + formatSubmissionStatusCardContentVisible(deviceUiState = oDeviceUiState) + assertThat(result, `is`(iResult)) + } + + private fun formatShowSubmissionStatusPositiveCardBase(oDeviceUIState: DeviceUIState?, iResult: Int) { + val result = formatShowSubmissionStatusPositiveCard(deviceUiState = oDeviceUIState) + assertThat(result, `is`(iResult)) + } + + private fun formatShowSubmissionDoneCardBase(oDeviceUIState: DeviceUIState?, iResult: Int) { + val result = formatShowSubmissionDoneCard(deviceUiState = oDeviceUIState) + assertThat(result, `is`(iResult)) + } + + private fun formatShowRiskStatusCardBase(oDeviceUIState: DeviceUIState?, iResult: Int) { + val result = formatShowRiskStatusCard(deviceUiState = oDeviceUIState) + assertThat(result, `is`(iResult)) + } + + private fun formatTestResultBase(oUiState: DeviceUIState?) { + mockkConstructor(SpannableStringBuilder::class) + + val spannableStringBuilder1 = + mockk(R.string.test_result_card_virus_name_text.toString()) + val spannableStringBuilder2 = + mockk(R.string.test_result_card_virus_name_text.toString() + " ") + val spannableStringBuilder3 = mockk("result") + + every { SpannableStringBuilder().append(any()) } returns spannableStringBuilder1 + every { spannableStringBuilder1.append(" ") } returns spannableStringBuilder2 + every { context.getString(R.string.test_result_card_virus_name_text) } returns R.string.test_result_card_virus_name_text.toString() + every { + spannableStringBuilder2.append( + any(), + any(), + Spannable.SPAN_EXCLUSIVE_INCLUSIVE + ) + } returns spannableStringBuilder3 + + val result = formatTestResult(uiState = oUiState) + assertThat(result, `is`(spannableStringBuilder3 as Spannable?)) + } + + + @Test + fun formatTestResultSpinnerVisible() { + formatTestResultSpinnerVisibleBase(oUiStateState = null, iResult = View.VISIBLE) + formatTestResultSpinnerVisibleBase(oUiStateState = ApiRequestState.FAILED, iResult = View.VISIBLE) + formatTestResultSpinnerVisibleBase(oUiStateState = ApiRequestState.IDLE, iResult = View.VISIBLE) + formatTestResultSpinnerVisibleBase(oUiStateState = ApiRequestState.STARTED, iResult = View.VISIBLE) + formatTestResultSpinnerVisibleBase(oUiStateState = ApiRequestState.SUCCESS, iResult = View.GONE) + } + + @Test + fun formatTestResultVisible() { + formatTestResultVisibleBase(oUiStateState = null, iResult = View.GONE) + formatTestResultVisibleBase(oUiStateState = ApiRequestState.FAILED, iResult = View.GONE) + formatTestResultVisibleBase(oUiStateState = ApiRequestState.IDLE, iResult = View.GONE) + formatTestResultVisibleBase(oUiStateState = ApiRequestState.STARTED, iResult = View.GONE) + formatTestResultVisibleBase(oUiStateState = ApiRequestState.SUCCESS, iResult = View.VISIBLE) + } + + @Test + fun formatTestResultStatusText() { + formatTestResultStatusTextBase( + oUiState = null, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = context.getString(R.string.test_result_card_status_negative) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.PAIRED_ERROR, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = context.getString(R.string.test_result_card_status_positive) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = context.getString(R.string.test_result_card_status_positive) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + formatTestResultStatusTextBase( + oUiState = DeviceUIState.UNPAIRED, + iResult = context.getString(R.string.test_result_card_status_invalid) + ) + } + + @Test + fun formatTestResultStatusColor() { + formatTestResultStatusColorBase(oUiState = null, iResult = context.getColor(R.color.colorTextSemanticRed)) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = context.getColor(R.color.colorTextSemanticGreen) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.PAIRED_ERROR, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + formatTestResultStatusColorBase( + oUiState = DeviceUIState.UNPAIRED, + iResult = context.getColor(R.color.colorTextSemanticRed) + ) + } + + @Test + fun formatTestStatusIcon() { + formatTestStatusIconBase(oUiState = null) + formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_NEGATIVE) + formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_ERROR) + formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_NO_RESULT) + formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE) + formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN) + formatTestStatusIconBase(oUiState = DeviceUIState.SUBMITTED_FINAL) + formatTestStatusIconBase(oUiState = DeviceUIState.SUBMITTED_INITIAL) + formatTestStatusIconBase(oUiState = DeviceUIState.UNPAIRED) + } + + @Test + fun formatTestResultPendingStepsVisible() { + formatTestResultPendingStepsVisibleBase(oUiState = null, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.PAIRED_ERROR, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.VISIBLE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.GONE) + formatTestResultPendingStepsVisibleBase(oUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatTestResultNegativeStepsVisible() { + formatTestResultNegativeStepsVisibleBase(oUiState = null, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.VISIBLE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.PAIRED_ERROR, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.GONE) + formatTestResultNegativeStepsVisibleBase(oUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatTestResultPositiveStepsVisible() { + formatTestResultPositiveStepsVisibleBase(oUiState = null, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.PAIRED_ERROR, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE, iResult = View.VISIBLE) + formatTestResultPositiveStepsVisibleBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = View.VISIBLE + ) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.GONE) + formatTestResultPositiveStepsVisibleBase(oUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatTestResultInvalidStepsVisible() { + formatTestResultInvalidStepsVisibleBase(oUiState = null, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.PAIRED_ERROR, iResult = View.VISIBLE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.GONE) + formatTestResultInvalidStepsVisibleBase(oUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatSubmissionStatusCardContentTitleText() { + formatSubmissionStatusCardContentTitleTextBase( + oUiState = null, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = context.getString(R.string.submission_status_card_title_available) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.PAIRED_ERROR, + iResult = context.getString(R.string.submission_status_card_title_available) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + formatSubmissionStatusCardContentTitleTextBase( + oUiState = DeviceUIState.UNPAIRED, + iResult = context.getString(R.string.submission_status_card_title_pending) + ) + } + + @Test + fun formatSubmissionStatusCardContentBodyText() { + formatSubmissionStatusCardContentBodyTextBase( + oUiState = null, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = context.getString(R.string.submission_status_card_body_negative) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.PAIRED_ERROR, + iResult = context.getString(R.string.submission_status_card_body_invalid) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + formatSubmissionStatusCardContentBodyTextBase( + oUiState = DeviceUIState.UNPAIRED, + iResult = context.getString(R.string.submission_status_card_body_pending) + ) + } + + @Test + fun formatSubmissionStatusCardContentStatusTextVisible() { + formatSubmissionStatusCardContentStatusTextVisibleBase(oUiState = null, iResult = View.GONE) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.PAIRED_ERROR, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = View.GONE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = View.GONE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = View.GONE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = View.GONE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase( + oUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = View.GONE + ) + formatSubmissionStatusCardContentStatusTextVisibleBase(oUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatSubmissionStatusCardContentIcon() { + formatSubmissionStatusCardContentIconBase(oUiState = null) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.PAIRED_NEGATIVE) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.PAIRED_ERROR) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.PAIRED_NO_RESULT) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.SUBMITTED_FINAL) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.SUBMITTED_INITIAL) + formatSubmissionStatusCardContentIconBase(oUiState = DeviceUIState.UNPAIRED) + } + + @Test + fun formatSubmissionStatusCardFetchingVisible() { + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = null, + bUiStateState = null, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = null, + bUiStateState = ApiRequestState.SUCCESS, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = null, + bUiStateState = ApiRequestState.STARTED, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = null, + bUiStateState = ApiRequestState.IDLE, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = null, + bUiStateState = ApiRequestState.FAILED, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = false, + bUiStateState = ApiRequestState.SUCCESS, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = false, + bUiStateState = ApiRequestState.STARTED, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = false, + bUiStateState = ApiRequestState.IDLE, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = false, + bUiStateState = ApiRequestState.FAILED, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = true, + bUiStateState = ApiRequestState.SUCCESS, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = true, + bUiStateState = ApiRequestState.STARTED, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = true, + bUiStateState = ApiRequestState.IDLE, + iResult = View.GONE + ) + formatSubmissionStatusCardFetchingVisibleBase( + bDeviceRegistered = true, + bUiStateState = ApiRequestState.FAILED, + iResult = View.VISIBLE + ) + } + + @Test + fun formatSubmissionStatusCardContentVisible() { + formatSubmissionStatusCardContentVisibleBase(oDeviceUiState = null, iResult = View.GONE) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.PAIRED_NEGATIVE, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.PAIRED_ERROR, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.PAIRED_NO_RESULT, + iResult = View.VISIBLE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.PAIRED_POSITIVE, + iResult = View.GONE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = View.GONE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.SUBMITTED_FINAL, + iResult = View.GONE + ) + formatSubmissionStatusCardContentVisibleBase( + oDeviceUiState = DeviceUIState.SUBMITTED_INITIAL, + iResult = View.GONE + ) + formatSubmissionStatusCardContentVisibleBase(oDeviceUiState = DeviceUIState.UNPAIRED, iResult = View.GONE) + + } + + @Test + fun formatShowSubmissionStatusPositiveCard() { + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = null, iResult = View.GONE) + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.GONE) + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = DeviceUIState.PAIRED_ERROR, iResult = View.GONE) + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.GONE) + formatShowSubmissionStatusPositiveCardBase( + oDeviceUIState = DeviceUIState.PAIRED_POSITIVE, + iResult = View.VISIBLE + ) + formatShowSubmissionStatusPositiveCardBase( + oDeviceUIState = DeviceUIState.PAIRED_POSITIVE_TELETAN, + iResult = View.VISIBLE + ) + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatShowSubmissionStatusPositiveCardBase( + oDeviceUIState = DeviceUIState.SUBMITTED_INITIAL, + iResult = View.GONE + ) + formatShowSubmissionStatusPositiveCardBase(oDeviceUIState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatShowSubmissionDoneCard() { + formatShowSubmissionDoneCardBase(oDeviceUIState = null, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.PAIRED_ERROR, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.PAIRED_POSITIVE, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.PAIRED_POSITIVE_TELETAN, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.SUBMITTED_FINAL, iResult = View.VISIBLE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.GONE) + formatShowSubmissionDoneCardBase(oDeviceUIState = DeviceUIState.UNPAIRED, iResult = View.GONE) + } + + @Test + fun formatShowRiskStatusCard() { + formatShowRiskStatusCardBase(oDeviceUIState = null, iResult = View.VISIBLE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.PAIRED_NEGATIVE, iResult = View.VISIBLE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.PAIRED_ERROR, iResult = View.VISIBLE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.PAIRED_NO_RESULT, iResult = View.VISIBLE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.PAIRED_POSITIVE, iResult = View.GONE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.PAIRED_POSITIVE_TELETAN, iResult = View.GONE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.SUBMITTED_FINAL, iResult = View.GONE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.SUBMITTED_INITIAL, iResult = View.VISIBLE) + formatShowRiskStatusCardBase(oDeviceUIState = DeviceUIState.UNPAIRED, iResult = View.VISIBLE) + } + + @Test + fun formatTestResult() { + formatTestResultBase(oUiState = null) + formatTestResultBase(oUiState = DeviceUIState.PAIRED_NEGATIVE) + formatTestResultBase(oUiState = DeviceUIState.PAIRED_ERROR) + formatTestResultBase(oUiState = DeviceUIState.PAIRED_NO_RESULT) + formatTestResultBase(oUiState = DeviceUIState.PAIRED_POSITIVE) + formatTestResultBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN) + formatTestResultBase(oUiState = DeviceUIState.SUBMITTED_FINAL) + formatTestResultBase(oUiState = DeviceUIState.SUBMITTED_INITIAL) + formatTestResultBase(oUiState = DeviceUIState.UNPAIRED) + } + + + @After + fun cleanUp() { + unmockkAll() + } +} \ No newline at end of file From 06c09af22cce11fb041ad141aab3f21410196a0f Mon Sep 17 00:00:00 2001 From: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Date: Tue, 9 Jun 2020 10:33:04 +0200 Subject: [PATCH 08/17] exchanged formatter (#296) --- Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml index bfca5283e93..e325ad032f5 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml @@ -51,7 +51,7 @@ android:layout_height="@dimen/icon_size_button" android:contentDescription="@{@string/accessibility_back}" android:src="@{@drawable/ic_close}" - android:tint="@{FormatterRiskHelper.formatStableIconColor(tracingViewModel.riskLevel)}" + android:tint="@{FormatterRiskHelper.formatStableTextColor(tracingViewModel.riskLevel)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> From f8c06fae6edae585bcf89448df8320fc8eac1be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20M=C3=B6ller?= Date: Tue, 9 Jun 2020 10:54:52 +0200 Subject: [PATCH 09/17] Fix Streaming for KeyFiles (#300) Signed-off-by: d067928 --- .../de/rki/coronawarnapp/http/service/DistributionService.kt | 2 ++ .../de/rki/coronawarnapp/util/security/VerificationKeys.kt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/service/DistributionService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/service/DistributionService.kt index 9eef5b91ff6..1571fc4b66c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/service/DistributionService.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/service/DistributionService.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.http.service import okhttp3.ResponseBody import retrofit2.http.GET +import retrofit2.http.Streaming import retrofit2.http.Url interface DistributionService { @@ -12,6 +13,7 @@ interface DistributionService { @GET suspend fun getHourIndex(@Url url: String): List + @Streaming @GET suspend fun getKeyFiles(@Url url: String): ResponseBody diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/VerificationKeys.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/VerificationKeys.kt index 6eb6c1e8d34..454dd6294fe 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/VerificationKeys.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/VerificationKeys.kt @@ -38,8 +38,8 @@ class VerificationKeys { .isEmpty() .also { if (BuildConfig.DEBUG) { - if (it) Log.d(TAG, "export is valid") - else Log.d(TAG, "export is invalid") + if (it) Log.d(TAG, "export is invalid") + else Log.d(TAG, "export is valid") } } } From 8e5a65ff9002ee8b33e21b951759b62148ff924c Mon Sep 17 00:00:00 2001 From: oemerb <66002424+oemerb@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:20:22 +0200 Subject: [PATCH 10/17] version bumped to 0.8.10 (#303) --- Corona-Warn-App/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 179b21c5758..e3f9189b691 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -32,8 +32,8 @@ android { applicationId 'de.rki.coronawarnapp' minSdkVersion 23 targetSdkVersion 29 - versionCode 16 - versionName "0.8.9" + versionCode 17 + versionName "0.8.10" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField "String", "DOWNLOAD_CDN_URL", "\"$DOWNLOAD_CDN_URL\"" From 517c94d18fa9910492255c5216d5f8fcc4b9a467 Mon Sep 17 00:00:00 2001 From: AlexanderAlferov <64849422+AlexanderAlferov@users.noreply.github.com> Date: Tue, 9 Jun 2020 12:29:20 +0300 Subject: [PATCH 11/17] Feature/background work (#293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Background work quick fix * Formatting * Background work improved * Formatting * Refactoring * Refactoring * Allow background data usage * Added isBackgroundRestricted * One timer for background and manual update Removed data saver check for background check * Removed separate timer for manual update button * Refactoring * Merge remote-tracking branch 'origin/dev' into feature/background-work # Conflicts: # Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml Co-authored-by: Jakob Möller --- .../rki/coronawarnapp/risk/TimeVariables.kt | 6 +-- .../de/rki/coronawarnapp/storage/LocalData.kt | 23 ---------- .../storage/SettingsRepository.kt | 18 ++++++-- .../de/rki/coronawarnapp/timer/TimerHelper.kt | 42 ++++++++----------- .../rki/coronawarnapp/ui/main/MainFragment.kt | 3 +- .../ui/riskdetails/RiskDetailsFragment.kt | 3 +- .../ui/viewmodel/SettingsViewModel.kt | 15 +++++-- .../ui/viewmodel/TracingViewModel.kt | 2 + .../coronawarnapp/util/ConnectivityHelper.kt | 37 ++-------------- .../util/formatter/FormatterRiskHelper.kt | 19 +++++++-- .../DiagnosisKeyRetrievalOneTimeWorker.kt | 12 +++++- .../main/res/layout/fragment_risk_details.xml | 2 +- .../src/main/res/layout/include_risk_card.xml | 2 +- 13 files changed, 83 insertions(+), 101 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/TimeVariables.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/TimeVariables.kt index 6f687165077..f12edb1310c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/TimeVariables.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/TimeVariables.kt @@ -103,11 +103,9 @@ object TimeVariables { /** * Delay in milliseconds for manual key retrieval process - * Internal requirements: 2 hours = 7200000 milliseconds - * Test value: 1 minute + * Internal requirements: 24 hours = 1000 * 60 * 60 * 24 milliseconds */ - // todo exchange with real value (currently 120 min) - private const val MANUAL_KEY_RETRIEVAL_DELAY = 60000L + private const val MANUAL_KEY_RETRIEVAL_DELAY = 1000 * 60 * 60 * 24 /** * Getter function for [MANUAL_KEY_RETRIEVAL_DELAY] diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt index fc0d79de672..c7ae6841f57 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt @@ -316,29 +316,6 @@ object LocalData { } } - /** - * Gets the last timestamp the user manually triggered the key retrieval process - * - * @return Long - */ - fun lastTimeManualDiagnosisKeysRetrieved(): Long = getSharedPreferenceInstance().getLong( - CoronaWarnApplication.getAppContext() - .getString(R.string.preference_timestamp_manual_diagnosis_keys_retrieval), - 0L - ) - - /** - * Sets the last timestamp the user manually triggered the key retrieval process - */ - fun lastTimeManualDiagnosisKeysRetrieved(value: Long) = - getSharedPreferenceInstance().edit(true) { - putLong( - CoronaWarnApplication.getAppContext() - .getString(R.string.preference_timestamp_manual_diagnosis_keys_retrieval), - value - ) - } - /**************************************************** * EXPOSURE NOTIFICATION DATA ****************************************************/ diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt index 308385ca1c5..77b7402d34c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt @@ -25,9 +25,7 @@ object SettingsRepository { val isConnectionEnabled = MutableLiveData(true) val isBluetoothEnabled = MutableLiveData(true) val isBackgroundJobEnabled = MutableLiveData(true) - - // TODO should go to a formatter - val manualKeyRetrievalText = MutableLiveData() + val manualKeyRetrievalTime = MutableLiveData() /** * Get the current notifications state. Only relevant for the ui. @@ -103,4 +101,18 @@ object SettingsRepository { fun updateBackgroundJobEnabled(value: Boolean) { isBackgroundJobEnabled.postValue(value) } + + /** + * Refresh manual key retrieval button status + */ + fun updateManualKeyRetrievalEnabled(value: Boolean) { + isManualKeyRetrievalEnabled.postValue(value) + } + + /** + * Refresh manual key retrieval button status + */ + fun updateManualKeyRetrievalTime(value: Long) { + manualKeyRetrievalTime.postValue(value) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/timer/TimerHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/timer/TimerHelper.kt index 5cd98a330c0..95b1d72057e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/timer/TimerHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/timer/TimerHelper.kt @@ -2,13 +2,12 @@ package de.rki.coronawarnapp.timer import android.util.Log import de.rki.coronawarnapp.BuildConfig -import de.rki.coronawarnapp.CoronaWarnApplication -import de.rki.coronawarnapp.R import de.rki.coronawarnapp.risk.TimeVariables import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.storage.SettingsRepository -import de.rki.coronawarnapp.util.TimeAndDateExtensions.millisecondsToHMS -import java.util.Date +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import org.joda.time.Instant import java.util.Timer import java.util.concurrent.atomic.AtomicBoolean import kotlin.concurrent.fixedRateTimer @@ -54,13 +53,16 @@ object TimerHelper { * * @return Long * - * @see LocalData.lastTimeManualDiagnosisKeysRetrieved + * @see LocalData.lastTimeDiagnosisKeysFromServerFetch * @see TimeVariables.getManualKeyRetrievalDelay */ private fun getManualKeyRetrievalTimeLeft(): Long { - val lastTime = LocalData.lastTimeManualDiagnosisKeysRetrieved() - val currentTime = Date(System.currentTimeMillis()).time - return TimeVariables.getManualKeyRetrievalDelay() - (currentTime - lastTime) + if (LocalData.lastTimeDiagnosisKeysFromServerFetch() == null) return 0 + + val currentDate = DateTime(Instant.now(), DateTimeZone.getDefault()) + val lastFetch = DateTime(LocalData.lastTimeDiagnosisKeysFromServerFetch(), DateTimeZone.getDefault()) + + return TimeVariables.getManualKeyRetrievalDelay() - (currentDate.millis - lastFetch.millis) } /** @@ -71,8 +73,6 @@ object TimerHelper { * @see SettingsRepository.isManualKeyRetrievalEnabled */ fun startManualKeyRetrievalTimer() { - LocalData.lastTimeManualDiagnosisKeysRetrieved(Date(System.currentTimeMillis()).time) - SettingsRepository.isManualKeyRetrievalEnabled.postValue(false) checkManualKeyRetrievalTimer() } @@ -96,6 +96,9 @@ object TimerHelper { logTimerException(e) } } + if (!isManualKeyRetrievalOnTimer.get()) { + SettingsRepository.updateManualKeyRetrievalEnabled(true) + } } /** @@ -104,25 +107,16 @@ object TimerHelper { * Else - update text with timer HMS format * * @see getManualKeyRetrievalTimeLeft - * @see SettingsRepository.isManualKeyRetrievalEnabled - * @see SettingsRepository.manualKeyRetrievalText + * @see SettingsRepository.updateManualKeyRetrievalEnabled + * @see SettingsRepository.updateManualKeyRetrievalTime * @see de.rki.coronawarnapp.util.TimeAndDateExtensions.millisecondsToHMS */ private fun onManualKeyRetrievalTimerTick() { - val context = CoronaWarnApplication.getAppContext() val timeDifference = getManualKeyRetrievalTimeLeft() val result = timeDifference <= 0 - SettingsRepository.isManualKeyRetrievalEnabled.postValue(result) - if (result) { - stopManualKeyRetrievalTimer() - SettingsRepository.manualKeyRetrievalText.postValue( - context.getString(R.string.risk_card_button_update) - ) - } else { - val hmsCooldownTime = timeDifference.millisecondsToHMS() - val cooldownText = context.getString(R.string.risk_card_button_cooldown).format(hmsCooldownTime) - SettingsRepository.manualKeyRetrievalText.postValue(cooldownText) - } + SettingsRepository.updateManualKeyRetrievalEnabled(result) + SettingsRepository.updateManualKeyRetrievalTime(timeDifference) + if (result) stopManualKeyRetrievalTimer() } /** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt index 74c9fa100a2..b0107c9f2ce 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainFragment.kt @@ -125,9 +125,8 @@ class MainFragment : BaseFragment() { doNavigate(MainFragmentDirections.actionMainFragmentToRiskDetailsFragment()) } binding.mainRisk.riskCardButtonUpdate.setOnClickListener { - tracingViewModel.refreshRiskLevel() tracingViewModel.refreshDiagnosisKeys() - TimerHelper.startManualKeyRetrievalTimer() + settingsViewModel.updateManualKeyRetrievalEnabled(false) } binding.mainRisk.riskCardButtonEnableTracing.setOnClickListener { doNavigate(MainFragmentDirections.actionMainFragmentToSettingsTracingFragment()) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/riskdetails/RiskDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/riskdetails/RiskDetailsFragment.kt index b553dcfadb1..e739b669987 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/riskdetails/RiskDetailsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/riskdetails/RiskDetailsFragment.kt @@ -71,9 +71,8 @@ class RiskDetailsFragment : BaseFragment() { (activity as MainActivity).goBack() } binding.riskDetailsButtonUpdate.setOnClickListener { - tracingViewModel.refreshRiskLevel() tracingViewModel.refreshDiagnosisKeys() - TimerHelper.startManualKeyRetrievalTimer() + settingsViewModel.updateManualKeyRetrievalEnabled(false) } binding.riskDetailsButtonEnableTracing.setOnClickListener { doNavigate( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt index 66a1be13a5c..14d6e5aa659 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt @@ -34,11 +34,11 @@ class SettingsViewModel : ViewModel() { val isManualKeyRetrievalEnabled: LiveData = SettingsRepository.isManualKeyRetrievalEnabled /** - * Update button on the Risk Card and in the Risk Details live text + * Manual update button timer value * - * @see SettingsRepository.manualKeyRetrievalText + * @see SettingsRepository.manualKeyRetrievalTime */ - val manualKeyRetrievalText: LiveData = SettingsRepository.manualKeyRetrievalText + val manualKeyRetrievalTime: LiveData = SettingsRepository.manualKeyRetrievalTime /** * Refresher and toggles for settings @@ -104,4 +104,13 @@ class SettingsViewModel : ViewModel() { fun updateBackgroundJobEnabled(value: Boolean) { SettingsRepository.updateBackgroundJobEnabled(value) } + + /** + * Update manual key button enabled + * + * @param value + */ + fun updateManualKeyRetrievalEnabled(value: Boolean) { + SettingsRepository.updateManualKeyRetrievalEnabled(value) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/TracingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/TracingViewModel.kt index 1d088b48ca6..5e7d07cd597 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/TracingViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/TracingViewModel.kt @@ -10,6 +10,7 @@ import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.storage.ExposureSummaryRepository import de.rki.coronawarnapp.storage.RiskLevelRepository import de.rki.coronawarnapp.storage.TracingRepository +import de.rki.coronawarnapp.timer.TimerHelper import de.rki.coronawarnapp.transaction.RiskLevelTransaction import kotlinx.coroutines.launch import java.util.Date @@ -79,6 +80,7 @@ class TracingViewModel : ViewModel() { fun refreshDiagnosisKeys() { this.viewModelScope.launch { TracingRepository.refreshDiagnosisKeys() + TimerHelper.startManualKeyRetrievalTimer() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt index aba5ccaa6a5..317e0290eae 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt @@ -122,37 +122,6 @@ object ConnectivityHelper { } } - /** - * Checks if background jobs are enabled - * - * @param context the context - * - * @return Boolean - * - * @see isDataSaverEnabled - * @see isBackgroundRestricted - */ - fun isBackgroundJobEnabled(context: Context): Boolean { - return !(isDataSaverEnabled(context) || isBackgroundRestricted(context)) - } - - /** - * For API level 24+ check if data saver is enabled - * Else always return false - * - * @param context the context - * - * @return Boolean - * - * @see ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED - */ - private fun isDataSaverEnabled(context: Context): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - connectivityManager.restrictBackgroundStatus != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED - } else false - } - /** * For API level 28+ check if background is restricted * Else always return false @@ -163,11 +132,11 @@ object ConnectivityHelper { * * @see isBackgroundRestricted */ - private fun isBackgroundRestricted(context: Context): Boolean { + fun isBackgroundJobEnabled(context: Context): Boolean { val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - return activityManager.isBackgroundRestricted - } else return false + return !activityManager.isBackgroundRestricted + } else return true } /** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt index b1923762115..12a3a2bea71 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt @@ -10,6 +10,7 @@ import android.view.View import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R import de.rki.coronawarnapp.risk.RiskLevelConstants +import de.rki.coronawarnapp.util.TimeAndDateExtensions.millisecondsToHMS import java.util.Date /*Texter*/ @@ -256,7 +257,6 @@ fun formatTimeFetched( * * @param riskLevelScore * @param isBackgroundJobEnabled - * @param nextUpdate * @return */ fun formatNextUpdate( @@ -545,7 +545,20 @@ fun formatButtonUpdateEnabled(enabled: Boolean?): Boolean { return enabled ?: true } -fun formatButtonUpdateText(updateButtonText: String?): String { +/** + * Change the manual update button text according to current timer + * + * @param time + * @return String + */ +fun formatButtonUpdateText( + time: Long +): String { val appContext = CoronaWarnApplication.getAppContext() - return updateButtonText ?: appContext.getString(R.string.risk_card_button_update) + if (time <= 0) { + return appContext.getString(R.string.risk_card_button_update) + } else { + val hmsCooldownTime = time.millisecondsToHMS() + return appContext.getString(R.string.risk_card_button_cooldown).format(hmsCooldownTime) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt index 5f31a256bc8..2789591c4ed 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt @@ -5,7 +5,11 @@ import android.util.Log import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import de.rki.coronawarnapp.BuildConfig +import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import org.joda.time.Instant /** * One time diagnosis key retrieval work @@ -36,7 +40,13 @@ class DiagnosisKeyRetrievalOneTimeWorker(val context: Context, workerParams: Wor } var result = Result.success() try { - RetrieveDiagnosisKeysTransaction.start() + val currentDate = DateTime(Instant.now(), DateTimeZone.getDefault()) + val lastFetch = DateTime(LocalData.lastTimeDiagnosisKeysFromServerFetch(), DateTimeZone.getDefault()) + if (LocalData.lastTimeDiagnosisKeysFromServerFetch() == null || + currentDate.withTimeAtStartOfDay() != lastFetch.withTimeAtStartOfDay() + ) { + RetrieveDiagnosisKeysTransaction.start() + } } catch (e: Exception) { result = Result.retry() } diff --git a/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml index e325ad032f5..9c2671cf316 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_risk_details.xml @@ -283,7 +283,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:enabled="@{FormatterRiskHelper.formatButtonUpdateEnabled(settingsViewModel.isManualKeyRetrievalEnabled())}" - android:text="@{FormatterRiskHelper.formatButtonUpdateText(settingsViewModel.manualKeyRetrievalText)}" + android:text="@{FormatterRiskHelper.formatButtonUpdateText(settingsViewModel.manualKeyRetrievalTime)}" android:visibility="@{FormatterRiskHelper.formatDetailsButtonUpdateVisibility(settingsViewModel.isBackgroundJobEnabled(), tracingViewModel.riskLevel)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/include_risk_card.xml b/Corona-Warn-App/src/main/res/layout/include_risk_card.xml index 437af070345..215479d306e 100644 --- a/Corona-Warn-App/src/main/res/layout/include_risk_card.xml +++ b/Corona-Warn-App/src/main/res/layout/include_risk_card.xml @@ -254,7 +254,7 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" android:enabled="@{FormatterRiskHelper.formatButtonUpdateEnabled(settingsViewModel.isManualKeyRetrievalEnabled())}" - android:text="@{FormatterRiskHelper.formatButtonUpdateText(settingsViewModel.manualKeyRetrievalText)}" + android:text="@{FormatterRiskHelper.formatButtonUpdateText(settingsViewModel.manualKeyRetrievalTime)}" android:visibility="@{FormatterRiskHelper.formatButtonUpdateVisibility(tracingViewModel.riskLevel, settingsViewModel.isBackgroundJobEnabled(), showDetails)}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" From bbf8c69b0197b614276ed624a1b9f9b7c72b78ab Mon Sep 17 00:00:00 2001 From: Hee Tatt Ooi Date: Tue, 9 Jun 2020 11:37:50 +0200 Subject: [PATCH 12/17] bugfix for version check (#298) * bugfix for version check * added comment * merged if conditions * newline to reduce line length --- .../coronawarnapp/update/VersionComparator.kt | 26 +++++++++++++++---- ...erviceTest.kt => VersionComparatorTest.kt} | 14 +++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/{VerificationServiceTest.kt => VersionComparatorTest.kt} (76%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/update/VersionComparator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/update/VersionComparator.kt index 1df0b5dde05..2a706d3e1a0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/update/VersionComparator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/update/VersionComparator.kt @@ -1,7 +1,20 @@ package de.rki.coronawarnapp.update +/** + * Helper to compare 2 version strings + */ object VersionComparator { + /** + * Checks if currentVersion is older than versionToCompareTo + * + * Expected input format: .. + * major, minor and patch are Int + * + * @param currentVersion + * @param versionToCompareTo + * @return true if currentVersion is older than versionToCompareTo, else false + */ fun isVersionOlder(currentVersion: String, versionToCompareTo: String): Boolean { var isVersionOlder = false @@ -19,12 +32,15 @@ object VersionComparator { if (versionToCompareMajor > currentVersionMajor) { isVersionOlder = true - } else if (versionToCompareMinor > currentVersionMinor) { - isVersionOlder = true - } else if (versionToComparePatch > currentVersionPatch) { - isVersionOlder = true + } else if (versionToCompareMajor == currentVersionMajor) { + if (versionToCompareMinor > currentVersionMinor) { + isVersionOlder = true + } else if ((versionToCompareMinor == currentVersionMinor) && + (versionToComparePatch > currentVersionPatch) + ) { + isVersionOlder = true + } } - return isVersionOlder } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VerificationServiceTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VersionComparatorTest.kt similarity index 76% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VerificationServiceTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VersionComparatorTest.kt index 28cf86195c8..5b92e22d288 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VerificationServiceTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/VersionComparatorTest.kt @@ -4,7 +4,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.`is` import org.junit.Test -class VerificationServiceTest { +class VersionComparatorTest { @Test fun testVersionMajorOlder() { @@ -47,4 +47,16 @@ class VerificationServiceTest { val result = VersionComparator.isVersionOlder("1.0.1", "1.0.1") assertThat(result, `is`(false)) } + + @Test + fun testIfMajorIsNewerButMinorSmallerNumber() { + val result = VersionComparator.isVersionOlder("3.1.0", "1.2.0") + assertThat(result, `is`(false)) + } + + @Test + fun testIfMinorIsNewerButPatchSmallerNumber() { + val result = VersionComparator.isVersionOlder("1.3.1", "1.2.4") + assertThat(result, `is`(false)) + } } From c763f5cf0fb4b855a0d51fda20907b8428fd0e82 Mon Sep 17 00:00:00 2001 From: Fabian-K Date: Tue, 9 Jun 2020 12:00:45 +0200 Subject: [PATCH 13/17] Bullet Point Lists (#297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added further info section to negative test result * bullet points as custom view with proper styling * moved BulletPointList.kt to proper package Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Co-authored-by: Jakob Möller --- .../coronawarnapp/ui/view/BulletPointList.kt | 28 +++++++++++++++ .../src/main/res/drawable/bullet_point.xml | 5 +++ .../include_submission_done_content.xml | 2 +- ... include_submission_done_further_info.xml} | 6 ++-- .../res/layout/include_submission_intro.xml | 11 +++--- .../layout/include_submission_test_result.xml | 12 +++++++ ...sion_test_result_negative_further_info.xml | 34 +++++++++++++++++++ .../res/layout/view_bullet_point_entry.xml | 30 ++++++++++++++++ .../src/main/res/values-de/strings.xml | 4 --- .../src/main/res/values-en/strings.xml | 4 --- Corona-Warn-App/src/main/res/values/attrs.xml | 4 +++ .../src/main/res/values/dimens.xml | 9 ++++- .../src/main/res/values/strings.xml | 25 +++++++++++--- 13 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/BulletPointList.kt create mode 100644 Corona-Warn-App/src/main/res/drawable/bullet_point.xml rename Corona-Warn-App/src/main/res/layout/{include_further_info.xml => include_submission_done_further_info.xml} (88%) create mode 100644 Corona-Warn-App/src/main/res/layout/include_submission_test_result_negative_further_info.xml create mode 100644 Corona-Warn-App/src/main/res/layout/view_bullet_point_entry.xml diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/BulletPointList.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/BulletPointList.kt new file mode 100644 index 00000000000..3459e2adf54 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/BulletPointList.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.withStyledAttributes +import de.rki.coronawarnapp.R + +class BulletPointList(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) { + + private lateinit var entries: Array + + init { + orientation = VERTICAL + + context.withStyledAttributes(attrs, R.styleable.BulletPointList) { + entries = getTextArray(R.styleable.BulletPointList_entries) + } + + entries.forEachIndexed { i, entry -> + // add point entry + inflate(context, R.layout.view_bullet_point_entry, this) + // set content + this.getChildAt(i).findViewById(R.id.bullet_point_content).text = entry + } + } +} diff --git a/Corona-Warn-App/src/main/res/drawable/bullet_point.xml b/Corona-Warn-App/src/main/res/drawable/bullet_point.xml new file mode 100644 index 00000000000..9b3021520e3 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/bullet_point.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_done_content.xml b/Corona-Warn-App/src/main/res/layout/include_submission_done_content.xml index 94660425ba4..b8b3c762067 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_done_content.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_done_content.xml @@ -63,7 +63,7 @@ app:riskLevel="@{RiskLevelConstants.INCREASED_RISK}" /> - diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_intro.xml b/Corona-Warn-App/src/main/res/layout/include_submission_intro.xml index c60a1ff19ec..be603c13924 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_intro.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_intro.xml @@ -46,18 +46,15 @@ app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_intro_headline" /> - + app:layout_constraintTop_toBottomOf="@id/submission_intro_text" /> diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml b/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml index 74da8120113..62eae1e3dcb 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml @@ -65,6 +65,17 @@ app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_subtitle" /> + + + diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_test_result_negative_further_info.xml b/Corona-Warn-App/src/main/res/layout/include_submission_test_result_negative_further_info.xml new file mode 100644 index 00000000000..7beff057f55 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/include_submission_test_result_negative_further_info.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/view_bullet_point_entry.xml b/Corona-Warn-App/src/main/res/layout/view_bullet_point_entry.xml new file mode 100644 index 00000000000..ffdcf0ce6a0 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/view_bullet_point_entry.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 06182633a83..783a35db2a1 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -602,8 +602,6 @@ "So funktioniert das Corona-Warn-System" "Damit die Corona-Warn-App funktioniert, sind wir auf die Mithilfe von Corona-positiven Personen angewiesen.\n\nDa nur verschlüsselte Zufallscodes ausgetauscht werden, bleiben Sie unerkannt. Sie können jetzt wie folgt vorgehen:\n" - -
  • "Wenn Sie positiv getestet wurden, können Sie andere warnen."
  • "\n\n"
  • "Wenn Ihnen für einen positiven Test eine TAN mitgeteilt wurde, können Sie diese für die Registrierung des Tests nutzen."
  • "\n\n"
  • "Wenn Sie keine TAN haben, können Sie diese telefonisch anfragen."
  • "Weiter" @@ -678,8 +676,6 @@ "Bitte isolieren Sie sich von anderen Personen." "Weitere Infos:" - -
  • "Ihre Quarantänezeit beträgt im Regelfall 14 Tage. Beobachten und erfassen Sie genau, wie sich ihre Krankheitszeichen entwickeln."
  • "\n\n"
  • "Sie werden von Ihrem Gesundheitsamt gebeten, eine Liste Ihrer Kontaktpersonen zu erstellen. Dabei sollen alle Personen erfasst werden, mit denen Sie in den zwei Tagen vor Erkrankungsbeginn engen Kontakt (unter 2 Meter, direktes Gespräch) über insgesamt 15 Minuten hatten."
  • "\n\n"
  • "Bitte denken Sie hier auch besonders an Personen, die nicht automatisch durch die App informiert werden, da sie kein Smartphone habe oder die App nicht installiert haben."
  • "\n\n"
  • "Auch wenn Sie keine Krankheitszeichen mehr haben und sich wieder gesund fühlen, könnten Sie noch ansteckend sein."
  • "Fertig" diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index 5592444a829..09fe5b18930 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -604,8 +604,6 @@ "This is how the Corona-Warn-App works" "For the app to work well, we are relying on the support of people who have tested positive. Since only encrypted random IDs are exchanged, you remain anonymous. You can now proceed as follows:\n" - - "If you tested positive, you can warn others. If you were given a TAN for a positive result, you can use this to register the test. If you do not have a TAN, you can request one by telephone." "Next" @@ -680,8 +678,6 @@ "Please isolate yourself from other people." "Other information:" - - "Your quarantine period is usually 14 days. Please observe your symptoms and monitor how they develop. You will be asked by your public health authority to create a list of people you have had contact with. This should include all people with whom you have had close contact with (less than 2 meters, face-to-face conversation) for over 15 minutes in the two days before you developed symptoms. Please think especially about the people that will not be notified directly by the app since they don\'t have a device, or haven\'t installed the app." "Done" diff --git a/Corona-Warn-App/src/main/res/values/attrs.xml b/Corona-Warn-App/src/main/res/values/attrs.xml index 3f51f3ce05d..b7e4364179a 100644 --- a/Corona-Warn-App/src/main/res/values/attrs.xml +++ b/Corona-Warn-App/src/main/res/values/attrs.xml @@ -17,4 +17,8 @@ + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/dimens.xml b/Corona-Warn-App/src/main/res/values/dimens.xml index e466e666342..248a374e5e5 100644 --- a/Corona-Warn-App/src/main/res/values/dimens.xml +++ b/Corona-Warn-App/src/main/res/values/dimens.xml @@ -63,7 +63,6 @@ 40dp 25dp 8dp - 0dp @dimen/spacing_huge @@ -98,5 +97,13 @@ 240dp 120dp + + 4dp + 8sp + @dimen/spacing_small + @dimen/spacing_tiny + @dimen/spacing_normal + + 0dp 0dp diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 23b3d6bf41a..039a4110f9b 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -2705,12 +2705,16 @@ as modifying the License. So funktionert das\nCorona-Warn-System Damit die Corona-Warn-App funktioniert, sind wir auf die Mithilfe von Corona-positiven Personen angewiesen.\n\nDa nur verschlüsselte Zufallscodes ausgetauscht werden, bleiben Sie unerkannt. Sie können jetzt wie folgt vorgehen:\n - -
  • Wenn Sie positiv getestet wurden, können Sie andere warnen.
  • \n\n
  • Wenn Ihnen für einen positiven Test eine TAN mitgeteilt wurde , können Sie diese für die Registrierung des Tests nutzen.
  • \n\n
  • Wenn Sie keine TAN haben, können Sie diese telefonisch anfragen.
  • Weiter Ein positiver Testbefund wird verschlüsselt ins System übermittelt, das nun andere Nutzerinnen und Nutzer warnt. + + + Wenn Sie positiv getestet wurden, können Sie andere warnen. + Wenn Ihnen für einen positiven Test eine TAN mitgeteilt wurde, können Sie diese für die Registrierung des Tests nutzen. + Wenn Sie keine TAN haben, können Sie diese telefonisch anfragen. + Wurden Sie getestet? So funktioniert das Corona-Warn-System @@ -2788,8 +2792,13 @@ as modifying the License. Isolieren Sie sich von anderen Personen. Weitere Infos: - -
  • Ihre Quarantänezeit beträgt im Regelfall 14 Tage. Beobachten und erfassen Sie genau, wie sich ihre Krankheitszeichen entwickeln.
  • \n\n
  • Sie werden von Ihrem Gesundheitsamt gebeten, eine Liste Ihrer Kontaktpersonen zu erstellen. Dabei sollen alle Personen erfasst werden, mit denen Sie in den zwei Tagen vor Erkrankungsbeginn engen Kontakt (unter 2 Meter, direktes Gespräch) über insgesamt 15 Minuten hatten.
  • \n\n
  • Bitte denken Sie hier auch besonders an Personen, die nicht automatisch durch die App informiert werden, da sie kein Smartphone oder die App haben.
  • \n\n
  • Auch wenn Sie keine Krankheitszeichen mehr haben und sich wieder gesund fühlen, könnten Sie noch ansteckend sein.
  • + + + Ihre Quarantänezeit beträgt im Regelfall 14 Tage. Beobachten und erfassen Sie genau, wie sich ihre Krankheitszeichen entwickeln. + Sie werden von Ihrem Gesundheitsamt gebeten, eine Liste Ihrer Kontaktpersonen zu erstellen. Dabei sollen alle Personen erfasst werden, mit denen Sie in den zwei Tagen vor Erkrankungsbeginn engen Kontakt (unter 2 Meter, direktes Gespräch) über insgesamt 15 Minuten hatten. + Bitte denken Sie hier auch besonders an Personen, die nicht automatisch durch die App informiert werden, da sie kein Smartphone oder die App haben. + Auch wenn Sie keine Krankheitszeichen mehr haben und sich wieder gesund fühlen, könnten Sie noch ansteckend sein. + Fertig @@ -2882,6 +2891,14 @@ as modifying the License. Auswertung nicht möglich Ergebnis liegt noch nicht vor + + Weitere Infos: + + + Sie haben trotzdem gesundheitliche Beschwerden? Wenn Sie sich sehr krankfühlen und/oder Ihre Beschwerden sich verschlechtert haben, wenden Sie sich bitte an Ihre/-n Hausärztin/Hausarzt. + Bleiben Sie bis zu Ihrer Genesung trotzdem zu Hause. Falls Sie sich durch eine andere Infektion geschwächt mit dem Coronavirus (SARS-CoV-2) infizieren, kann dies zu schwereren Krankheitsverläufen führen. + Gehen Sie nicht krank zur Arbeit, um andere Personen nicht zu gefährden. Falls sich Ihre Symptome verschlechtern, kann die Notwendigkeit eines weiteren SARS-CoV-2-Tests bestehen. + OK From 0010817263a1aa4579a9591b5ee1edb0dfd6b2f3 Mon Sep 17 00:00:00 2001 From: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Date: Tue, 9 Jun 2020 12:10:18 +0200 Subject: [PATCH 14/17] Feature: Webviews (#305) * extracted strings to html * added webviews * ktlint * fixes --- .../src/main/assets/privacy_de.html | 778 +++++++ .../src/main/assets/technical_de.html | 348 +++ Corona-Warn-App/src/main/assets/terms_de.html | 812 +++++++ .../information/InformationPrivacyFragment.kt | 7 + .../InformationTechnicalFragment.kt | 7 + .../information/InformationTermsFragment.kt | 7 + .../onboarding/OnboardingPrivacyFragment.kt | 8 + .../rki/coronawarnapp/util/AssetConstants.kt | 6 + .../layout/fragment_information_privacy.xml | 9 +- .../layout/fragment_information_technical.xml | 9 +- .../res/layout/fragment_information_terms.xml | 11 +- .../layout/fragment_onboarding_privacy.xml | 20 +- .../src/main/res/values-de/strings.xml | 1 - .../src/main/res/values-en/strings.xml | 1 - .../src/main/res/values/strings.xml | 1949 +---------------- .../FormatterSubmissionHelperTest.kt | 11 +- 16 files changed, 2011 insertions(+), 1973 deletions(-) create mode 100644 Corona-Warn-App/src/main/assets/privacy_de.html create mode 100644 Corona-Warn-App/src/main/assets/technical_de.html create mode 100644 Corona-Warn-App/src/main/assets/terms_de.html create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AssetConstants.kt diff --git a/Corona-Warn-App/src/main/assets/privacy_de.html b/Corona-Warn-App/src/main/assets/privacy_de.html new file mode 100644 index 00000000000..96968cbe1cd --- /dev/null +++ b/Corona-Warn-App/src/main/assets/privacy_de.html @@ -0,0 +1,778 @@ +

    + In dieser Datenschutzerklärung erfahren Sie, welche Daten bei der Nutzung + der Corona-Warn-App erhoben werden, wie sie verwendet werden und welche + Datenschutzrechte Sie haben. +

    +

    + Damit diese Datenschutzerklärung für alle Nutzer verständlich ist, bemühen + wir uns um eine möglichst einfache und untechnische Darstellung. +

    +

    + 1. Wer stellt Ihnen diese App zur Verfügung? +

    +

    + Der Anbieter der Corona-Warn-App (im Folgenden die „App“) + ist das Robert Koch-Institut, Nordufer 20, 13353 Berlin (im Folgenden „RKI“). +

    +

    + Das RKI ist auch der datenschutzrechtlich Verantwortliche für die + Verarbeitung von personenbezogenen Daten der App-Nutzer. +

    +

    + Den Datenschutzbeauftragten des RKI erreichen Sie unter der oben genannten + Anschrift (zu Händen „Behördlicher Datenschutzbeauftragter“) und per E-Mail + an: datenschutz@rki.de). +

    +

    + 2. Ist die Nutzung der App freiwillig? +

    +

    + Die Benutzung der App basiert ausschließlich auf Freiwilligkeit. Es ist + daher allein Ihre Entscheidung, ob und wie Sie die App nutzen. +

    +

    + Auch wenn die Installation und die Benutzung der App freiwillig sind, + müssen Sie nach dem erstmaligen Aufruf der App gegenüber dem RKI durch + Antippen des Buttons „Risiko-Ermittlung aktivieren“ zustimmen, dass die App + im Rahmen der Risiko-Ermittlung Ihre personenbezogenen Daten + (einschließlich Gesundheitsdaten, falls für Sie ein Infektionsrisiko + ermittelt wird) verarbeiten darf. Dies ist erforderlich, da andernfalls die + App nicht auf die Kontaktaufzeichnungs-Funktion Ihres Smartphones zugreifen + kann. Sie können die Risiko-Ermittlung jedoch jederzeit über den + Schieberegler innerhalb der App deaktivieren. In diesem Fall stehen Ihnen + nicht alle Funktionen der App zur Verfügung. Gesonderte Einwilligungen sind + darüber hinaus für die Datenverarbeitung der folgenden Funktionen + erforderlich: +

    +
      +
    • + Test registrieren (siehe Ziffer 6 b.) +
    • +
    • + Testergebnis teilen (siehe Ziffer 6 c.) +
    • +
    +

    + Die Datenverarbeitung im Rahmen dieser Funktionen wird in den folgenden + Abschnitten näher beschrieben. +

    +

    + 3. Auf welcher Rechtsgrundlage werden Ihre Daten verarbeitet? +

    +

    + Das RKI verarbeitet Ihre personenbezogenen Daten grundsätzlich nur auf + Grundlage einer von Ihnen erteilten Einwilligung nach Artikel 6 Absatz 1 + Satz 1 Buchstabe a und Artikel 9 Absatz 2 Buchstabe a der + Datenschutzgrundverordnung (DSGVO). Sie können eine von Ihnen erteilte + Einwilligung jederzeit widerrufen. Weitere Informationen zu Ihrem + Widerrufsrecht und Hinweise, wie Sie dieses ausüben können, finden Sie + unter Ziffer 11. +

    +

    + 4. An wen richtet sich die App? +

    +

    + Die App richtet sich an Personen, die sich in Deutschland aufhalten und + mindestens 16 Jahre alt sind. +

    +

    + 5. Welche personenbezogenen Daten werden verarbeitet? +

    +

    + Die App ist so konzipiert, dass so wenig personenbezogene Daten wie möglich + verarbeitet werden. Das bedeutet zum Beispiel, dass die App keine Daten + erfasst, die es dem RKI oder anderen Nutzern ermöglichen, auf Ihre + Identität, Ihren Gesundheitsstatus oder Ihren Standort zu schließen. Zudem + verzichtet die App bewusst auf jegliche Erfassung oder Analyse Ihres + Nutzungsverhaltens durch Tracking-Tools. +

    +

    + Die von der App verarbeiteten Daten lassen sich den folgenden Kategorien + zuordnen: +

    +

    + a. Zugriffsdaten +

    +

    + Bei jedem Abruf einer auf einem Server hinterlegten Datei fallen + Zugriffsdaten an. Im Einzelnen werden bei jedem Abruf folgende Daten + verarbeitet: +

    +
      +
    • + IP-Adresse +
    • +
    • + Datum und Uhrzeit des Abrufs (Zeitstempel) +
    • +
    • + übertragene Datenmenge (bzw. Paketlänge) +
    • +
    • + Meldung über erfolgreichen Abruf +
    • +
    • + anfragende Domain +
    • +
    • + verwendetes Betriebssystem +
    • +
    • + Gerätetyp (Smartphone), der Hersteller und das Modell Ihres Smartphones + (z. B. „iPhone 7“ oder „Galaxy“) +
    • +
    +

    + Diese Zugriffsdaten werden nur zur Sicherung und Aufrechterhaltung der + technischen Infrastruktur verarbeitet. Sie werden dabei nicht als Nutzer + der App persönlich identifiziert und es kann kein Nutzungsprofil erstellt + werden. +

    +

    + Zugriffsdaten fallen an, wenn Sie die folgenden Funktionen nutzen bzw. + aktivieren: +

    +
      +
    • + Risiko-Ermittlung +
    • +
    • + Test registrieren +
    • +
    • + Testergebnis teilen +
    • +
    +

    + b. Begegnungsdaten +

    +

    + Wenn Sie auf Ihrem Smartphone die betriebssystemseitige Funktion zur + Aufzeichnung von Kontakten zu anderen Nutzern aktivieren, versendet Ihr + Smartphone per Bluetooth kontinuierlich zufallsgenerierte Kennnummern (im + Folgenden „Zufalls-IDs“), die von anderen Smartphones in + Ihrer Nähe mit ebenfalls aktivierter Kontaktaufzeichnung empfangen werden + können. Umgekehrt empfängt Ihr Smartphone auch die Zufalls-IDs der anderen + Smartphones. Zu den von anderen Smartphones empfangenen Zufalls-IDs werden + von der Kontaktaufzeichnungs-Funktion Ihres Smartphones zusätzlich folgende + Begegnungsdaten aufgezeichnet und gespeichert: +

    +
      +
    • + Datum des Kontakts +
    • +
    • + Dauer des Kontakts +
    • +
    • + Bluetooth-Signalstärke des Kontakts +
    • +
    +

    + Die eigenen und von anderen Smartphones empfangenen Zufalls-IDs und die + weiteren Begegnungsdaten (Datum, Dauer, Signalstärke) werden von Ihrem + Smartphone in einem Kontaktprotokoll der Kontaktaufzeichnungs-Funktion + erfasst und dort für 14 Tage gespeichert. +

    +

    + Die Kontaktaufzeichnungs-Funktion heißt bei Android-Smartphones + "Benachrichtigungen zu möglicher Begegnung mit Infizierten" und bei iPhones + „COVID-19-Kontaktprotokoll“. Wir weisen Sie darauf hin, dass diese + Kontaktaufzeichnungs-Funktionen kein Bestandteil der App, sondern ein + integraler Bestandteil des Betriebssystems Ihres Smartphones sind. Die + Kontaktaufzeichnungs-Funktion wird Ihnen daher von Apple (iPhones) bzw. + Google (Android-Smartphones) bereitgestellt und unterliegt dementsprechend + den Datenschutzbestimmungen dieser Unternehmen. Die betriebssystemseitige + Datenverarbeitung im Rahmen der Kontaktaufzeichnungs-Funktion liegt + außerhalb des Einflussbereichs des RKI. +

    +

    + Weitere Informationen zu der Kontaktaufzeichnungs-Funktion von + Android-Smartphones finden Sie unter: + https://support.google.com/android/answer/9888358?hl=de + . +

    +

    + Weitere Informationen zu der Kontaktaufzeichnungs-Funktion von Apple finden + Sie in den Einstellungen Ihres iPhones unter "Datenschutz“ > „Health" + > „COVID-19-Kontaktprotokoll“. Bitte beachten Sie: Die + Kontaktaufzeichnungs-Funktion steht Ihnen nur zur Verfügung, wenn auf Ihrem + iPhone das Betriebssystem iOS ab Version 13.5 installiert ist. +

    +

    + Die vom Smartphone erzeugten und gespeicherten Begegnungsdaten werden von + der App nur verarbeitet, wenn die Risiko-Ermittlung aktiviert ist. +

    +

    + c. Gesundheitsdaten +

    +

    + Gesundheitsdaten sind alle Daten, die Informationen zum Gesundheitszustand + einer bestimmten Person enthalten. Dazu gehören nicht nur Angaben zu + früheren und aktuellen Krankheiten, sondern auch zu Krankheitsrisiken einer + Person (z. B. das Risiko, dass eine Person sich mit dem Corona-Virus + infiziert hat). +

    +

    + In den folgenden Fällen werden Ihre Gesundheitsdaten verarbeitet: +

    +
      +
    • + Wenn die Risiko-Ermittlung erkennt, dass Sie möglicherweise Kontakt zu + einer Person hatten, die sich mit dem Corona-Virus infiziert hat. +
    • +
    • + Wenn Sie einen Test registrieren. +
    • +
    • + Wenn Sie ein positives Testergebnis teilen. +
    • +
    +

    + 6. Funktionen der App +

    +

    + a. Risiko-Ermittlung +

    +

    + Die Risiko-Ermittlung ist die Kernfunktion der App. Sie dient dazu, + mögliche Kontakte zu mit dem Corona-Virus infizierten anderen Nutzern der + App nachzuverfolgen, das infolge für Sie bestehende Infektionsrisiko zu + bewerten und Ihnen, basierend auf dem für Sie ermittelten Risikowert, + Verhaltens- und Gesundheitshinweise bereitzustellen. +

    +

    + Wenn Sie die Risiko-Ermittlung aktivieren, ruft die App von den + Serversystemen der App im Hintergrundbetrieb mehrmals täglich (oder wenn + Sie auf „Aktualisieren“ tippen) eine Liste mit Zufalls-IDs von Nutzern ab, + die ein positives Testergebnis geteilt haben. Die App gibt die Zufalls-IDs + an die Kontaktaufzeichnungs-Funktion Ihres Smartphones weiter, welche diese + dann mit den im Kontaktprotokoll Ihres Smartphones gespeicherten + Zufalls-IDs abgleicht. Wenn die Kontaktaufzeichnungs-Funktion Ihres + Smartphones eine Übereinstimmung feststellt, übergibt sie der App die + Begegnungsdaten (Datum, Dauer, Signalstärke), nicht jedoch die Zufalls-ID + des betreffenden Kontakts. +

    +

    + Im Fall eines Kontakts werden die von der Kontaktaufzeichnungs-Funktion + übergebenen Begegnungsdaten von der App analysiert, um Ihr individuelles + Infektionsrisiko zu ermitteln. Der Bewertungsalgorithmus, der festlegt, wie + die Begegnungsdaten interpretiert werden (z. B. welchen Einfluss die Dauer + eines Kontakts auf das Infektionsrisiko hat) basiert auf den aktuellen + wissenschaftlichen Erkenntnissen. Bei neuen Erkenntnissen kann der + Bewertungsalgorithmus daher durch das RKI aktualisiert werden, indem die + Einstellungen für den Bewertungsalgorithmus neu gesetzt werden. Die + Einstellungen für den Bewertungsalgorithmus werden dann zusammen mit der + Liste mit den Zufalls-IDs an die App übermittelt. +

    +

    + Die Ermittlung des Infektionsrisikos findet ausschließlich lokal auf Ihrem + Smartphone statt, das heißt die Daten werden offline verarbeitet. Das + ermittelte Infektionsrisiko wird ebenfalls ausschließlich in der App + gespeichert und an keine anderen Empfänger (auch nicht an das RKI, Apple, + Google und sonstige Dritte) weitergegeben. +

    +

    + Rechtsgrundlage der oben beschriebenen Verarbeitung Ihrer Zugriffsdaten, + Begegnungsdaten und ggf. Gesundheitsdaten (sofern für Sie ein + Infektionsrisiko ermittelt wird) ist Ihre Einwilligung, die Sie bei der + Aktivierung der Risiko-Ermittlung erteilt haben. +

    +

    + b. Test registrieren +

    +

    + Wenn Sie auf eine Infektion mit dem Corona-Virus getestet wurden, können + Sie den Test in der App registrieren, indem Sie den QR-Code, den Sie von + Ihrem Arzt bzw. der Testeinrichtung erhalten haben, in der App einscannen. + Die App informiert Sie dann, sobald das Testergebnis des Labors vorliegt. +

    +

    + Dies setzt voraus, dass das Testlabor an das Serversystem der App + angeschlossen ist und Sie im Rahmen der Testdurchführung gesondert in die + Übermittlung Ihres Testergebnisses durch das Labor an das Serversystem der + App (Testergebnis-Datenbank) eingewilligt haben. Testergebnisse von + Laboren, die nicht an das Serversystem der App angeschlossen sind, können + nicht in der App angezeigt werden. Wenn Sie keinen QR-Code erhalten haben, + ist das Testlabor nicht angeschlossen. In diesem Fall können Sie diese + Funktion nicht nutzen. +

    +

    + Testregistrierung +

    +

    + Damit Sie das Testergebnis in der App erhalten können, müssen Sie den + durchgeführten Test zunächst in der App registrieren. Hierzu erhalten Sie + von Ihrem Arzt bzw. der Testeinrichtung im Rahmen der Probenentnahme einen + QR-Code. Dieser QR-Code enthält eine Kennzahl, die mit einem + QR-Code-Scanner ausgelesen werden kann. Zur Testregistrierung müssen Sie + den QR-Code in der App mit der Kamera Ihres Smartphones scannen. +

    +

    + Die aus dem QR-Code ausgelesene Kennzahl wird von der App dann gehasht, das + bedeutet, die Kennzahl wird nach einem bestimmten mathematischen Verfahren + so verfremdet, dass niemand mehr die dahinterstehende Kennzahl erkennen + kann. Sobald Ihr Smartphone eine Verbindung zum Internet hat, wird die App + die gehashte Kennzahl an die Serversysteme der App übermitteln. Im Gegenzug + erhält die App vom Serversystem einen Token, also einen digitalen + Zugangsschlüssel, der in der App gespeichert wird. Das Token ist auf dem + Serversystem mit der gehashten Kennzahl verknüpft. Die App löscht dann die + auf Ihrem Smartphone gehashte Kennzahl. Das Serversystem wird für jede + gehashte Kennzahl nur ein einziges Mal einen Token vergeben. Auf diese + Weise wird sichergestellt, dass Ihr QR-Code nicht von anderen Nutzern der + App für die Abfrage von Testergebnissen verwendet werden kann. +

    +

    + Die Registrierung Ihres Tests ist damit abgeschlossen. +

    +

    + Hinterlegung des Testergebnisses +

    +

    + Sobald dem Testlabor das Testergebnis vorliegt, hinterlegt es das Ergebnis + unter Angabe der gehashten Kennzahl in der vom RKI betriebenen + Testergebnis-Datenbank. Die Testergebnis-Datenbank wird vom RKI auf einem + speziellen Server innerhalb des Serversystems der App betrieben. Das + Testlabor erzeugt die gehashte Kennzahl ebenfalls auf Basis der an Sie im + ausgegebenen QR-Code enthaltenen Kennzahl unter Verwendung des gleichen + mathematischen Verfahrens, das auch die App einsetzt. +

    +

    + Abruf des Testergebnisses +

    +

    + Die App fragt bei dem Serversystem der App unter Verwendung des Tokens + regelmäßig den Status des registrierten Tests ab. Das Serversystem ordnet + das Token dann der gehashten Kennzahl zu und übermittelt diese an die + Testergebnis-Datenbank. Ist das Testergebnis dort mittlerweile abgelegt, + sendet die Testergebnis-Datenbank das Testergebnis an das Serversystem + zurück, der dieses ohne Kenntnisnahme des Inhalts an die App weiterleitet. +

    +

    + Im Fall eines positiven Testergebnis fordert die App beim Serversystem + unter erneuter Verwendung des Tokens eine TAN (Transaktionsnummer) an. Das + Serversystem ordnet das Token wieder der gehashten Kennzahl zu und fordert + von der Testergebnis-Datenbank eine Bestätigung an, dass zu der gehashten + Kennzahl ein positives Testergebnis vorliegt. Sofern die + Testergebnis-Datenbank dies bestätigt, erzeugt das Serversystem die TAN und + übermittelt sie an die App. Eine Kopie der TAN verbleibt auf dem + Serversystem. +

    +

    + Die TAN wird benötigt, um im Fall einer Übermittlung des positiven + Testergebnisses sicherzustellen, dass keine falschen Informationen an + andere Nutzer verteilt werden. +

    +

    + Rechtsgrundlage der oben beschriebenen Verarbeitung der zuvor genannten + Daten ist Ihre Einwilligung für die Funktion „Test registrieren“. +

    +

    + c. Testergebnis teilen +

    +

    + Wenn Sie Ihr positives Testergebnis teilen, um andere Nutzer zu warnen, + überträgt die App die von Ihrem Smartphone gespeicherten eigenen + Zufalls-IDs der letzten 14 Tage und die TAN an das Serversystem der App. + Dieses prüft zunächst, ob die TAN gültig ist und trägt Ihre Zufalls-IDs + sodann in die Liste der Zufalls-IDs von Nutzern, die ihr positives + Testergebnis geteilt haben, ein. Ihre Zufalls-IDs können nun von anderen + Nutzern im Rahmen der Risiko-Ermittlung heruntergeladen werden. +

    +

    + Wenn Sie Ihr Testergebnis nicht in der App abgerufen haben: +

    +

    + Auch wenn Sie ein positives Testergebnis nicht in der App abgerufen haben, + können Sie das Testergebnis per App teilen, um andere Nutzer zu warnen. In + diesem Fall fordert Sie die App zur Eingabe einer sogenannten TeleTAN auf, + die als TAN fungiert. +

    +

    + Für den Erhalt der TeleTAN können Sie die Hotline der Corona-Warn-App unter + der Nummer +49 (0)800 7540002 anrufen. Der dort für Sie zuständige + Ansprechpartner wird Ihnen zunächst am Telefon einige Fragen stellen, um + die Plausibilität Ihres Anrufs zu überprüfen. Diese Fragen dienen der + Vorbeugung einer missbräuchlichen Infektionsmeldung und daraus + resultierender fehlerhafter Warnungen und Risikowerte. Nach ausreichender + Beantwortung dieser Fragen werden Sie nach Ihrer Handy-/Telefonnummer + gefragt. Dies dient dazu, Sie später zurückrufen zu können, um Ihnen eine + TeleTAN zur Eingabe in der App mitzuteilen. Ihre Handy-/Telefonnummer wird + nur zu diesem Zweck vorübergehend gespeichert und spätestens innerhalb + einer Stunde gelöscht. +

    +

    + Nach Ihrem Anruf wird der Hotline-Mitarbeiter über einen speziellen Zugang + zum Serversystem der App eine TeleTAN generieren und Sie sodann anrufen, um + Ihnen die TeleTAN mitzuteilen. Wenn Sie die TeleTAN in der App eingeben, + wird die TeleTAN von der App zum Abgleich und Verifizierung an das + Serversystem der App zurückgesendet. Im Gegenzug erhält die App vom + Serversystem einen Token, also einen digitalen Zugangsschlüssel, der in der + App gespeichert wird. Mit diesem Token fordert die App beim Serversystem + dann eine TAN an. +

    +

    + Rechtsgrundlage dieser Verarbeitung Ihrer Zugriffsdaten und + Gesundheitsdaten (Zufalls-IDs, Testergebnis, TAN und ggf. TeleTAN) ist Ihre + Einwilligung für die Funktion „Testergebnis teilen“. +

    +

    + d. Informatorische Nutzung der App +

    +

    + Soweit Sie die App nur informatorisch nutzen, also keine der oben genannten + Funktionen der App verwenden und keine Daten eingeben, findet die + Verarbeitung ausschließlich lokal auf Ihrem Smartphone statt und es fallen + keine personenbezogenen Daten an. +

    +

    + 7. Welche Berechtigungen und Funktionen benötigt die App? +

    +

    + Die App benötigt Zugriff auf verschiedene Funktionen und Schnittstellen + Ihres Smartphones. Dazu ist es erforderlich, dass Sie der App bestimmte + Berechtigungen erteilen. Die Berechtigungen sind von den verschiedenen + Herstellern unterschiedlich programmiert. So können z. B. + Einzelberechtigungen zu Berechtigungskategorien zusammengefasst sein, wobei + Sie der Berechtigungskategorie nur insgesamt zustimmen können. Bitte + beachten Sie, dass Sie im Falle der Ablehnung eines Zugriffs durch die App + keine oder nur wenige Funktionen der App nutzen können. +

    +

    + a. Technische Voraussetzungen (alle Smartphones) +

    +
      +
    • + Internet +
    • +
    +

    + Die App benötigt für die Funktionen Risiko-Ermittlung, Testergebnisse + erhalten und Testergebnis übermitteln eine Internetverbindung, um mit den + Serversystemen der App kommunizieren zu können. +

    +
      +
    • + Bluetooth +
    • +
    +

    + Die Bluetooth-Schnittstelle Ihres Smartphones muss aktiviert sein, damit + Ihr Smartphone Zufalls-IDs von anderen Smartphones erfassen und im + Kontaktprotokoll des Geräts speichern kann. +

    +
      +
    • + Kamera +
    • +
    +

    + Ihr Smartphone benötigt eine Kamera, um damit einen QR-Code im Rahmen der + Testregistrierung scannen können. +

    +
      +
    • + Hintergrundbetrieb +
    • +
    +

    + Die App nutzt den Hintergrundbetrieb (also wenn Sie die App nicht gerade + aktiv nutzen), um Ihr Risiko automatisch zu ermitteln und den Status eines + registrierten Tests abfragen zu können. Wenn Sie den Hintergrundbetrieb im + Betriebssystem Ihres Smartphones deaktivieren, müssen Sie alle Aktionen in + der App selbst starten. +

    +

    + b. Android-Smartphones +

    +

    + Wenn Sie ein Android-Gerät verwenden, müssen außerdem folgende + Systemfunktionen aktiviert sein: +

    +
      +
    • + Benachrichtigungen zu möglicher Begegnung mit COVID-19-Infizierten +
    • +
    +

    + Die Risiko-Ermittlung benötigt diese Funktion, da andernfalls kein + Kontaktprotokoll mit den Zufalls-IDs Ihrer Kontakte zur Verfügung steht. + Die Funktion muss innerhalb der App aktiviert werden, damit die App auf das + Kontaktprotokoll zugreifen darf. +

    +
      +
    • + Standortermittlung +
    • +
    +

    + Die Standortermittlung Ihres Smartphones muss aktiviert sein, damit Ihr + Gerät nach Bluetooth-Signalen anderer Smartphones sucht. Standortdaten + werden dabei jedoch nicht erhoben. +

    +
      +
    • + Benachrichtigung +
    • +
    +

    + Der Nutzer wird lokal über die Risiko-Ermittlung und vorhandene + Testergebnisse benachrichtigt. Die dafür notwendige + Benachrichtigungsfunktion ist im Betriebssystem bereits aktiviert. +

    +

    + Daneben benötigt die App folgende Berechtigungen: +

    +
      +
    • + Kamera +
    • +
    +

    + Die App benötigt Zugriff auf die Kamera, um bei der Testregistrierung den + QR-Code auslesen zu können. +

    +

    + c. iPhones (Apple iOS) +

    +

    + Wenn Sie ein iPhone verwenden, müssen folgende Systemfunktionen aktiviert + sein: +

    +
      +
    • + COVID-19-Kontaktprotokoll +
    • +
    +

    + Die Risiko-Ermittlung benötigt diese Funktion, da andernfalls kein + Kontaktprotokoll mit den Zufalls-IDs Ihrer Kontakte zur Verfügung steht. + Die Funktion muss innerhalb der App aktiviert werden, damit die App auf das + Kontaktprotokoll zugreifen darf. +

    +
      +
    • + Mitteilungen +
    • +
    +

    + Der Nutzer wird lokal über die Risiko-Ermittlung und vorhandene + Testergebnisse benachrichtigt. Mitteilungen müssen dafür aktiviert sein. +

    +

    + Die App benötigt zudem folgende Berechtigungen: +

    +
      +
    • + Kamera +
    • +
    +

    + Die App benötigt Zugriff auf die Kamera, um bei der Testregistrierung den + QR-Code auslesen zu können. +

    +

    + 8. Wann werden die Daten gelöscht? +

    +

    + Alle in der App gespeicherten Daten werden gelöscht, sobald sie für die + Funktionen der App nicht mehr benötigt werden: +

    +

    + a. Risiko-Ermittlung +

    +
      +
    • + Die Liste der Zufalls-IDs von Nutzern, die ein positives Testergebnis + geteilt haben, wird unmittelbar nach dem Abgleich mit den Zufalls-IDs + im Kontaktprotokoll Ihres Smartphones von der App gelöscht. +
    • +
    • + Auf die Löschung der Begegnungsdaten im Kontaktprotokoll Ihres + Smartphones (einschließlich Ihrer eigenen Zufalls-IDs) und die + Begegnungsdaten auf anderen Smartphones hat das RKI keinen Einfluss, da + diese Funktion von Apple bzw. Google bereitgestellt werden. Die + Löschung richtet sich nach den Festlegungen von Apple bzw. Google. + Zurzeit werden die Daten nach 14 Tagen automatisch gelöscht. Zudem + können Sie im Rahmen der von Apple und Google bereitgestellten + Funktionalitäten in den Systemeinstellungen Ihres Geräts gegebenenfalls + eine manuelle Löschung anstoßen. +
    • +
    • + Der in der App angezeigte Risikowert wird gelöscht, sobald ein neuer + Risikowert ermittelt worden ist. Ein neuer Risikowert wird in der Regel + ermittelt, nachdem die App eine neue Liste mit Zufalls-IDs erhalten + hat. +
    • +
    +

    + b. Test registrieren +

    +
      +
    • + Die gehashte Kennzahl wird auf dem Serversystem der App nach 21 Tagen + gelöscht. +
    • +
    • + Die gehashte Kennzahl und das Testergebnis in der + Testergebnis-Datenbank werden im Fall eines negativen Testergebnisses + unmittelbar nach dem Abruf des Testergebnisses und im Fall eines + positiven Testergebnissen unmittelbar nach dem Löschen der auf + Serversystem gespeicherten Kopie der TAN gelöscht (s.u.). +
    • +
    • + Das Token, das auf dem Serversystem gespeichert ist, wird nach 21 Tagen + gelöscht. +
    • +
    • + Das Token, das in der App gespeichert ist, wird nach Löschung der App + vom Smartphone oder nach dem Teilen des Testergebnisses gelöscht. +
    • +
    +

    + c. Testergebnis teilen +

    +
      +
    • + Die in der App geteilten eigenen Zufalls-IDs werden nach 14 Tagen vom + Serversystem gelöscht. +
    • +
    • + Die Kopie der TAN, die auf dem Serversystem gespeichert ist, wird nach + 21 Tagen gelöscht. +
    • +
    • + Die TAN, die in der App gespeichert ist, wird nach Teilen des + Testergebnisses gelöscht. +
    • +
    • + Die TeleTAN, die in der App gespeichert ist, wird nach Teilen des + Testergebnisses gelöscht. +
    • +
    • + Die TeleTAN, die auf dem Serversystem gespeichert ist, wird nach 21 + Tagen gelöscht. +
    • +
    • + Die TeleTAN, die dem Mitarbeiter der Hotline übermittelt wird, wird + dort direkt nach der telefonischen Weitergabe an Sie gelöscht. +
    • +
    • + Das Token, das auf dem Serversystem gespeichert ist, wird nach 21 Tagen + gelöscht. +
    • +
    • + Das Token, das in der App gespeichert ist, wird nach Teilen des + Testergebnisses gelöscht. +
    • +
    +

    + 9. An wen werden Ihre Daten weitergegeben? +

    +

    + Wenn Sie ein Testergebnis teilen, um andere Nutzer zu warnen, werden Ihre + Zufalls-IDs der letzten 14 Tage an die Apps der anderen Nutzer + weitergegeben. +

    +

    + Mit dem Betrieb und der Wartung eines Teils der technischen Infrastruktur + der App (z. B. Serversysteme, Hotline) hat das RKI die Deutsche Telekom AG + und die SAP Deutschland SE & Co. KG beauftragt, die insoweit als + Auftragsverarbeiter des RKI tätig werden (Artikel 28 DSGVO). +

    +

    + Im Übrigen gibt das RKI personenbezogene Daten, die im Zusammenhang mit der + Nutzung der App erhoben werden, nur an Dritte weiter, soweit das RKI + rechtlich dazu verpflichtet ist oder die Weitergabe im Falle von Angriffen + auf die technische Infrastruktur der App zur Rechts- oder Strafverfolgung + erforderlich ist. Eine Weitergabe in anderen Fällen erfolgt grundsätzlich + nicht. +

    +

    + 10. Werden Daten in ein Drittland übermittelt? +

    +

    + Die bei der Nutzung der App anfallenden Daten werden ausschließlich auf + Servern in Deutschland oder in einem anderem EU- oder EWR-Mitgliedsstaat + verarbeitet. +

    +

    + 11. Wiederruf von Einwilligungen +

    +

    + Ihnen steht das Recht zu, die in der App erteilten Einwilligungen gegenüber + dem RKI jederzeit mit Wirkung für die Zukunft zu widerrufen. Die + Rechtmäßigkeit der Verarbeitung bis zum Widerruf wird dadurch jedoch nicht + berührt. +

    +

    + Zum Widerruf Ihrer Einwilligung in die Risiko-Ermittlung können Sie die + Funktion über den Schieberegler innerhalb der App deaktivieren oder die App + löschen. Wenn Sie die Risiko-Ermittlung wieder nutzen möchten, können Sie + den Schieberegler erneut aktivieren oder die App erneut installieren. +

    +

    + Zum Widerruf Ihrer Einwilligung für die Funktion „Test registrieren“ können + Sie die Testregistrierung in der App löschen. Das Token zum Abruf des + Testergebnisses wird dann von Ihrem Gerät gelöscht. Weder das RKI noch das + Testlabor können die übermittelten Daten dann Ihrer App oder Ihrem + Smartphone zuordnen. Wenn Sie einen weiteren Test registrieren möchten, + werden Sie um eine neue Einwilligung gebeten. +

    +

    + Zum Widerruf Ihrer Einwilligung für die Funktion „Testergebnis teilen“ + müssen Sie die App löschen. Sämtliche Ihrer in der App gespeicherten + Zufalls-IDs werden dann entfernt und können Ihrem Smartphone nicht mehr + zugeordnet werden. Wenn Sie erneut ein Testergebnis melden möchten, können + Sie in der App erneut installieren und eine neue Einwilligung erteilen. + Alternativ können Sie Ihre eigenen Zufalls-IDs gegebenenfalls im Rahmen der + Kontaktaufzeichnungs-Funktion in den Systemeinstellungen Ihres Smartphones + löschen. Bitte beachten Sie, dass das RKI keine Möglichkeit hat, um Ihre + bereits übermittelten Zufalls-IDs unmittelbar aus den bereitgestellten + Listen und von Smartphones anderer Nutzer zu löschen. +

    +

    + 12. Ihre weiteren Datenschutzrechte +

    +

    + Soweit das RKI personenbezogene Daten von Ihnen verarbeitet, stehen Ihnen + außerdem folgende Datenschutzrechte zu: +

    +
      +
    • + die Rechte aus den Artikeln 15, 16, 17, 18, 20 und 21 DSGVO, +
    • +
    • + das Recht, den behördlichen + Datenschutzbeauftragten des RKI + zu kontaktieren und Ihr Anliegen vorzubringen (Artikel 38 Abs. 4 DSGVO) + und +
    • +
    • + das Recht, sich bei einer zuständigen Aufsichtsbehörde für den + Datenschutz zu beschweren. Dazu können Sie sich entweder an die + zuständige Aufsichtsbehörde an Ihrem Wohnort oder an die am Sitz des + RKI zuständige Behörde wenden. Die zuständige Aufsichtsbehörde für das + RKI ist der Bundesbeauftragte für den Datenschutz und die + Informationsfreiheit, Graurheindorfer Str. 153, 53117 Bonn. +
    • +
    +

    + Es wird darauf hingewiesen, dass die vorgenannten Rechte vom RKI nur + erfüllt werden können, wenn die Daten, auf die sich die geltend gemachten + Ansprüche beziehen, eindeutig Ihrer Person zugeordnet werden können. Dies + wäre nur möglich, wenn das RKI weitere personenbezogene Daten erhebt, die + eine eindeutige Zuordnung der oben genannten Daten zu Ihrer Person oder + Ihrem Smartphone erlaubt. Da dies für die Zwecke der App nicht erforderlich + – und auch nicht gewollt – ist, ist das RKI zu einer solchen zusätzlichen + Datenerhebung nicht verpflichtet (Artikel 11 Abs. 2 DSGVO). Zudem würde + dies dem erklärten Ziel zuwiderlaufen, die Datenverarbeitung im Rahmen der + App so datensparsam wie möglich durchzuführen. Aus diesem Hintergrund + werden die vorgenannten Datenschutzrechte aus den Artikeln 15, 16, 17, 18, + 20 und 21 DSGVO in der Regel nicht unmittelbar und nur mit zusätzlichen + Informationen zu Ihrer Person, die dem RKI nicht vorliegen, erfüllt werden + können. +

    +

    + Stand: 05.06.2020 +

    \ No newline at end of file diff --git a/Corona-Warn-App/src/main/assets/technical_de.html b/Corona-Warn-App/src/main/assets/technical_de.html new file mode 100644 index 00000000000..303c1d8fac3 --- /dev/null +++ b/Corona-Warn-App/src/main/assets/technical_de.html @@ -0,0 +1,348 @@ +

    ThirdPartyNotices

    +

    corona-warn-app/cwa-app-android uses third-party software or other resources that + may be distributed under licenses different from corona-warn-app/cwa-app-android + software. + In the event that we overlooked to list a required notice, please bring this + to our attention by contacting us via this email: + corona-warn-app.opensource@sap.com

    +

    Components:

    +

    Component: JUnit 4 + Licensor: The JUnit Team + Website: https://junit.org/junit4/ + License: Eclipse Public License 1.0

    +

    Component: Java Hamcrest + Licensor: www.hamcrest.org + Website: https://github.com/hamcrest/JavaHamcrest + License: BSD-style license

    +

    Component: android-database-sqlcipher + Licensor: Zetetic LLC + Website: https://www.zetetic.net/sqlcipher/ + License: BSD-style license

    +

    Component: guava + Licensor: Google + Website: https://github.com/google/guava + License: Apache 2.0

    +

    Component: joda-time + Licensor: Joda.org + Website: https://www.joda.org/joda-time/ + License: Apache 2.0

    +

    Component: Mockito + Licensor: Mockito contributors + Website: https://site.mockito.org/ + License: MIT

    +

    Component: MockK + Licensor: github.com/mockk + Website: https://github.com/mockk/mockk + License: Apache 2.0

    +

    Component: Robolectric + Licensor: Xtreme Labs, Pivotal Labs and Google Inc. + Website: https://robolectric.org/ + License: MIT

    +

    Component: RootBeer + Licensor: Scott Alexander-Bown, Mat Rollings + Website: https://github.com/scottyab/rootbeer + License: Apache 2.0

    +

    Component: Zxing + Licensor: zxing + Website: https://github.com/zxing/zxing/ + License: Apache 2.0

    +

    Component: ZXing Android Embedded + Licensor: ZXing authors, Journey Mobile + Website: https://github.com/journeyapps/zxing-android-embedded + License: Apache 2.0

    +

    Component: Detekt + Licensor: detekt + Website: https://detekt.github.io/detekt/ + License: Apache 2.0

    +

    Component: gson + Licensor: Google + Website: https://github.com/google/gson + License: Apache 2.0

    +

    Component: okhttp + Licensor: square + Website: https://square.github.io/okhttp/ + License: Apache 2.0

    +

    Component: kotlinx.coroutines + Licensor: Kotlin + Website: https://github.com/Kotlin/kotlinx.coroutines + License: Apache 2.0

    +

    Component: Ktlint Gradle + Licensor: Jonathan Leitschuh + Website: https://github.com/JLLeitschuh/ktlint-gradle + License: MIT

    +

    Component: Retrofit + Licensor: square + Website: https://square.github.io/retrofit/ + License: Apache 2.0

    +

    Component: Protobuf gradle plugin + Licensor: Google + Website: https://github.com/google/protobuf-gradle-plugin + License: BSD 3-Clause

    +

    Copyright (c) 2008-2020 Zetetic LLC + All rights reserved.

    +

    Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met:

    +
      * Redistributions of source code must retain the above copyright
    +  notice, this list of conditions and the following disclaimer.
    +  * Redistributions in binary form must reproduce the above copyright
    +  notice, this list of conditions and the following disclaimer in the
    +  documentation and/or other materials provided with the distribution.
    +  * Neither the name of the ZETETIC LLC nor the
    +  names of its contributors may be used to endorse or promote products
    +  derived from this software without specific prior written permission.
    +

    THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    +

    Apache License 2.0 (guava, joda-time, MockK, RootBeer, Zxing, ZXing Android Embedded, Detekt, + gson, okhttp, kotlinx.coroutines, Retrofit)

    +
                                 Apache License
    +                       Version 2.0, January 2004
    +                    https://www.apache.org/licenses/
    +

    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

    +
      +
    1. Definitions.

      +

      "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document.

      +

      "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License.

      +

      "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity.

      +

      "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License.

      +

      "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files.

      +

      "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types.

      +

      "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below).

      +

      "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof.

      +

      "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution."

      +

      "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work.

      +
    2. +
    3. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form.

      +
    4. +
    5. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed.

      +
    6. +
    7. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions:

      +

      (a) You must give any other recipients of the Work or

      +
      Derivative Works a copy of this License; and
      +
      +

      (b) You must cause any modified files to carry prominent notices

      +
      stating that You changed the files; and
      +
      +

      (c) You must retain, in the Source form of any Derivative Works

      +
      that You distribute, all copyright, patent, trademark, and
      +attribution notices from the Source form of the Work,
      +excluding those notices that do not pertain to any part of
      +the Derivative Works; and
      +
      +

      (d) If the Work includes a "NOTICE" text file as part of its

      +
      distribution, then any Derivative Works that You distribute must
      +include a readable copy of the attribution notices contained
      +within such NOTICE file, excluding those notices that do not
      +pertain to any part of the Derivative Works, in at least one
      +of the following places: within a NOTICE text file distributed
      +as part of the Derivative Works; within the Source form or
      +documentation, if provided along with the Derivative Works; or,
      +within a display generated by the Derivative Works, if and
      +wherever such third-party notices normally appear. The contents
      +of the NOTICE file are for informational purposes only and
      +do not modify the License. You may add Your own attribution
      +notices within Derivative Works that You distribute, alongside
      +or as an addendum to the NOTICE text from the Work, provided
      +that such additional attribution notices cannot be construed
      +as modifying the License.
      +
      +

      You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License.

      +
    8. +
    9. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions.

      +
    10. +
    11. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file.

      +
    12. +
    13. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License.

      +
    14. +
    15. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages.

      +
    16. +
    17. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability.

      +

      END OF TERMS AND CONDITIONS

      +
    18. +
    +

    The MIT License

    +

    Copyright (c) 2007 Mockito contributors

    +

    Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions:

    +

    The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software.

    +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.

    +

    The MIT License

    +

    Copyright (c) 2010 Xtreme Labs, Pivotal Labs and Google Inc.

    +

    Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions:

    +

    The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software.

    +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.

    +

    MIT License

    +

    Copyright (c) 2020 Jonathan Leitschuh

    +

    Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions:

    +

    The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software.

    +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE.

    +

    BSD 3-Clause (Protobuf gradle plugin)

    +

    Original work copyright (c) 2015, Alex Antonov. All rights reserved. + Modified work copyright (c) 2015, Google Inc. All rights reserved.

    +

    Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met:

    +
      +
    1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer.

      +
    2. +
    3. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution.

      +
    4. +
    5. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission.

      +
    6. +
    +

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    \ No newline at end of file diff --git a/Corona-Warn-App/src/main/assets/terms_de.html b/Corona-Warn-App/src/main/assets/terms_de.html new file mode 100644 index 00000000000..8df1539f4ec --- /dev/null +++ b/Corona-Warn-App/src/main/assets/terms_de.html @@ -0,0 +1,812 @@ +

    + 1. Welche Funktionen hat die APP? +

    +

    + 2. Was bedeutet niedriges oder erhöhtes Risiko? +

    +

    + 3. Risiko-Begegnung – was tun? +

    +

    + 4. Kein Infektionsschutz durch die App. +

    +

    + 5. Abrufen von Testergebnissen. +

    +

    + 6. Auslösen einer Warnung. +

    +

    + 7. Voraussetzungen für die Nutzung der App. +

    +

    + 8. Grenzen der App. +

    +

    + 9. Nutzungsrechte. +

    +

    + 10. Wichtige Hinweise zu Verfügbarkeit und Änderungen. +

    +

    + 11. Keine Gewährleistung. +

    +

    + 12. Besondere Bedingungen für die iOS-Version der App. +

    +

    + 13. Schlussbestimmungen. +

    +

    + Herausgeber der App ist das +

    +

    + Robert Koch-Institut +

    +

    + Nordufer 20 +

    +

    + 13353 Berlin ("RKI") +

    +

    + vertreten durch den Präsidenten. Für Fragen, Beschwerden und Ansprüche im + Zusammenhang mit diesen Nutzungsbedingungen können Sie das RKI telefonisch + unter +49 30 18754-5100 und per E-Mail unter CoronaWarnApp@rki.de + erreichen. +

    +

    + Bitte lesen Sie diese Nutzungsbedingungen sorgfältig. Diese + Nutzungsbedingungen regeln Ihre Nutzung der Corona Warn App einschließlich + der Nutzung zukünftiger Versionen (Patches, Updates und Upgrades) ("App") und + der Nutzung weiterer Dienste (" CWA-Dienste"), die + derzeit oder + zukünftig vom RKI im Zusammenhang mit der App bereitgestellt werden. Die + separate Datenschutzerklärung (siehe [Link]) ist + Bestandteil dieser Nutzungsbedingungen. Bitte lesen Sie auch die + Datenschutzerklärung sorgfältig. +

    +

    + Die App ist für die Nutzung auf iOS-Geräten im Apple App Store verfügbar + und für die Nutzung auf Android-Geräten bei Google Play. Allgemeine + Nutzungsbedingungen für Applikationen von Apple oder Google finden im + Verhältnis zum RKI keine Anwendung. Das RKI ist allein verantwortlich für + die Inhalte der App sowie deren Wartung und Pflege und für Ansprüche, die + Sie ggf. in Bezug auf die App haben. +

    +

    + Mit der Installation und Nutzung der App erklären Sie sich mit diesen + Nutzungsbedingungen einverstanden. Sollten Sie mit den Nutzungsbedingungen + nicht einverstanden sein, dürfen Sie die App nicht installieren oder nutzen + (bzw. müssen diese wieder löschen) und dürfen auch die die damit + verbundenen Dienste und Systeme nicht nutzen. Etwaige Rechte unter Open + Source-Lizenzen in Bezug auf den Quellcode der App bleiben hiervon + unberührt. +

    +

    + Sie sind für die Einhaltung dieser Nutzungsbedingungen auch dann + verantwortlich, wenn Sie das Endgerät, auf dem Sie die App installiert + haben, Dritten überlassen und diese die App verwenden. +

    +

    + Die App richtet sich an Personen, die mindestens 16 Jahre alt sind. + Personen unter 16 Jahren dürfen die App nur mit Zustimmung ihres/ihrer + Sorgeberechtigten verwenden. +

    +

    + 1. Welche Funktionen hat die APP? +

    +

    + Ziel der Corona Warn App ist es, SARS-CoV-2-Infektionsketten frühzeitig zu + durchbrechen. Personen sollen möglichst zuverlässig und schnell über + Begegnungen mit infizierten Personen und damit mögliche Übertragungen des + Virus informiert werden, damit sie sich freiwillig isolieren können, um + damit zu einer Eindämmung der SARS-CoV-2-Pandemie beizutragen. +

    +

    + Die wesentlichen Funktionen der App sind nachstehend beschrieben. Hierbei + handelt es sich um eine Beschreibung zu Ihrer Information und nicht um eine + Beschaffenheitsvereinbarung oder die Vereinbarung bestimmter Funktionen, + bitte beachten Sie die diesbezüglichen Hinweise und Vorbehalte unter Ziffer + 10. +

    +

    + Hintergrund +

    +

    + Die App läuft auf dem Endgerät im Hintergrund und speichert automatisiert + und verschlüsselt die Zufallscodes (rolling proximity identifier) + anderer in der Nähe befindlicher Endgeräte. In regelmäßigen Abständen holt + sich die App über die CWA-Dienste eine Liste der Zufallscodes (t emporary exposure + keys) der Personen, die sich freiwillig + infiziert gemeldet haben, und vergleicht diese mit den gespeicherten + Zufallscodes im Gerät, um eine Risiko-Begegnung zu ermitteln. +

    +

    + Die App kann nur Begegnungen mit Personen registrieren, die ihrerseits ein + Endgerät mit installierter App bei sich führen und alle Voraussetzungen für + die Nutzung der App erfüllen (siehe unten Ziffer 7). Begegnungen mit + anderen Personen kann die App nicht registrieren. +

    +

    + Risikobenachrichtigung +

    +

    + Bei einer festgestellten Risiko-Begegnung zu positiv getesteten Personen + erhalten Sie eine Benachrichtigung und verhaltensbezogene Empfehlungen. + Hier kann zum Beispiel die Kontaktaufnahme mit ärztlichem Fachpersonal, mit + dem zuständigen Gesundheitsamt und/oder die freiwillige häusliche Isolation + empfohlen werden. +

    +

    + Benachrichtigung über Testergebnisse +

    +

    + Ab dem Zeitpunkt der Probenabgabe für einen Test auf eine + SARS-CoV-2-Infektion können Sie über die App den digitalen + Testinformationsprozess starten und damit über das Testergebnis + benachrichtigt werden. Die App übermittelt lediglich das vom jeweiligen + Labor mitgeteilte Testergebnis. Das RKI ist weder für die Durchführung des + Tests noch für den Inhalt des Testergebnisses verantwortlich. +

    +

    + Infektfall +

    +

    + Im Fall eines positiven SARS-CoV-2-Befunds können Sie freiwillig die in der + App gespeicherten eigenen Zufallscodes der letzten 14 Tage als + Positivkennungen (diagnosis keys) veröffentlichen, damit andere + Personen, die die App nutzen, auf ihrem eigenen Endgerät abgleichen können, + ob sie mit Ihnen eine Risiko-Begegnung hatten. +

    +

    + Technische Beschreibung der App +

    +

    + Die technischen Funktionen der App sowie der damit verbundenen Dienste und + Systeme sind unter folgendem Link detailliert beschrieben: +

    +

    + https://github.com/Corona-Warn-App +

    +

    + Diese technische Beschreibung dient lediglich der Erläuterung und ist nicht + Bestandteil dieser Nutzungsbedingungen. Sie stellt auch keine + Beschaffenheitsvereinbarung in Bezug auf die App dar. +

    +

    + Weiterführende Informationen +

    +

    + Weiterführende Informationen zur App finden Sie unter folgendem Link: + [Link] +

    +

    + Weiterführende Informationen zur SARS-CoV-2-Pandemie finden Sie unter + folgendem Link: + https://www.zusammengegencorona.de/ +

    +

    + Diese weiterführenden Informationen dienen lediglich der Erläuterung und + sind nicht Bestandteil dieser Nutzungsbedingungen. +

    +

    + 2. Was bedeutet niedriges oder erhöhtes Risiko? +

    +

    + Die App berechnet auf Basis der festgestellten Begegnungen ein indikatives + individuelles Risiko. Hierbei werden Faktoren wie der Zeitraum seit der + Begegnung (days since exposure), die Dauer der Begegnung ( exposure duration), + die ungefähre Nähe zur infizierten Person, + basierend auf der gemessenen Dämpfung des Bluetooth-Signals (signal attenuation) sowie + ggf. Übertragungsrisiken ( transmission risk berücksichtigt. Auf dieser Basis wird + Ihnen in + der App entweder ein "niedriges Risiko" oder ein "erhöhtes Risiko" + angezeigt. Es handelt sich hierbei um einen rein indikativen Wert auf der + Grundlage der erfassten Daten. + + Eine Aussage über das Vorliegen einer tatsächlichen Infektion mit + SARS-CoV-2 ist hiermit nicht verbunden. + + Auch bei der Angabe eines "niedrigen Risikos" kann eine Infektion + vorliegen, während auch bei Angabe eines "erhöhten Risikos" eine Infektion + nicht gegeben sein kann. +

    +

    + + Andere Faktoren können Ihr persönliches Infektionsrisiko erheblich + beeinflussen. Diese werden von der App nicht berücksichtigt. + + Das sind insbesondere Ihre persönlichen Umstände, die äußeren Umstände + einer Risiko-Begegnung mit einer infizierten Person, Ihr persönliches + Verhalten sowie Begegnungen mit Dritten, die von der App nicht erfasst + wurden. Bitte beachten Sie die Hinweise in Ziffer 4. +

    +

    + 3. Risiko-Begegnung – was tun? +

    +

    + Wenn Sie über die App eine Benachrichtigung über eine festgestellte + Risiko-Begegnung mit infizierten Personen erhalten, erhalten Sie + verhaltensbezogene Empfehlungen in der Benachrichtigung. Diese Empfehlungen + sind rechtlich nicht verbindlich, ihre Befolgung wird aber vom RKI + empfohlen. Gesetzliche und vertragliche Pflichten sowie behördliche + Anordnungen für den Fall einer Risiko-Begegnung mit infizierten Personen + bleiben unberührt und müssen von Ihnen unabhängig von diesen Empfehlungen + befolgt werden. +

    +

    + + Innerhalb oder über die App erfolgt keinerlei medizinische Diagnose. + +

    +

    + + Die Benachrichtigung bedeutet nicht, dass Sie sich mit SARS-CoV-2 + infiziert haben. + + Sie besagt lediglich, dass sie während der letzten 14 Tage eine + Risiko-Begegnung mit einer anderen Person hatten, bei der eine + SARS-CoV-2-Infektion positiv festgestellt wurde. Hieraus ergibt sich für + Sie zunächst nur eine Möglichkeit, dass Sie sich ebenfalls infiziert haben + könnten. Die Einstufung des diesbezüglichen Risikos als "niedrig" oder + "erhöht" erfolgt allein auf der Basis der ermittelten Daten und lässt keine + Aussage über das tatsächliche Vorliegen einer Infektion zu. +

    +

    + + Die Benachrichtigung über eine Risiko-Begegnung kann unzutreffend sein. + + Die Risiko-Begegnung kann von Ihrem Endgerät beispielsweise zu einem + Zeitpunkt registriert worden sein, zu dem Sie sich nicht in der Nähe Ihres + Endgeräts aufgehalten haben oder während eine andere Person Ihr Endgerät + verwendet hat. Die Risiko-Begegnung kann auch aufgrund bestehender Grenzen + bei der Kontaktmessung fälschlicherweise registriert worden sein (siehe + unten Ziffer 8). +

    +

    + + 4. Kein Infektionsschutz durch die App +

    +

    + Die App dient der Unterbrechung von Infektionsketten. +

    +
      +
    • + + Die App schützt Sie nicht vor einer SARS-CoV-2-Infektion. + +
    • +
    • + + Die App reduziert Ihr persönliches Infektionsrisiko nicht. + +
    • +
    • + + Sie können sich auch mit SARS-CoV-2 infizieren, ohne dass die App + Sie über die Risiko-Begegnung mit der Person benachrichtigt, die + Sie infiziert hat: + +
    • +
    +

    + o Die App registriert nicht alle Ihre Begegnungen mit anderen Personen, + z.B. weil andere Personen die App nicht verwenden, Sie Ihr Endgerät nicht + immer bei sich tragen oder die App nicht immer in Betrieb haben oder weil + die Kontaktmessung gewissen Grenzen unterliegt (siehe unten Ziffer 8). +

    +

    + o Die App informiert Sie nur über festgestellte Risiko-Begegnungen mit + infizierten Personen. Das setzt voraus, dass die App die Begegnung mit der + infizierten Person registriert hat und die infizierte Person eine + Warnung über die App auslöst. Das Auslösen der Warnung ist freiwillig und + erfolgt möglicherweise nicht durch alle infizierten Personen. +

    +

    + Bitte halten Sie auch bei Verwendung der App die sonstigen + Vorsichtsmaßnahmen und behördlichen Anordnungen ein. Verlässliche + Informationen über die SARS-CoV-2-Pandemie und Vorsichtsmaßnahmen finden + Sie unter anderem auf: +

    +

    + www.infektionsschutz.de/coronavirus +

    +

    + www.zusammengegencorona.de +

    +

    + www.rki.de/covid-19 +

    +

    + Diese weiterführenden Informationen dienen lediglich Ihrer Information und + sind nicht Bestandteil dieser Nutzungsbedingungen. +

    +

    + Halten Sie sich auch bei Verwendung der App an die AHA-Regeln: +

    +

    + A + ✋ - Halten Sie mindestens 1,5m Abstand zu Ihren Mitmenschen +

    +

    + H + � - Waschen Sie sich regelmäßig für mindestens 20 Sekunden die Hände +

    +

    + A + � - Tragen Sie beim Einkauf und in öffentlichen Verkehrsmitteln eine + Alltagsmaske +

    +

    + So schützen Sie sich selbst und andere vor dem Virus. +

    +

    + 5. Abrufen von Testergebnissen +

    +

    + Sie dürfen über die App ausschließlich Ihre eigenen Testergebnisse abrufen. +

    +

    + Wenn Sie auf Testergebnisse warten und die CWA-Dienste nicht zur Verfügung + stehen oder der Abruf der Testergebnisse über die App aus sonstigen Gründen + nicht funktioniert, dann informieren Sie sich bitte über andere Kontaktwege + über das Testergebnis, z.B. über die jeweilige Teststelle wie Ihren + Hausarzt oder das örtliche Gesundheitsamt. Warten Sie nicht darauf, dass + die App wieder zur Verfügung steht. +

    +

    + 6. Auslösen einer Warnung +

    +

    + Sie können über die App andere Kontaktpersonen warnen, wenn Sie mit + SARS-CoV-2 infiziert wurden. Sie dürfen diese Warnung nur auslösen, wenn + + die Infektion durch einen Test in einem zugelassenen Testlabor positiv + bestätigt wurde. + +

    +

    + Falls das zugelassene Testlabor noch nicht an die CWA-Dienste angeschlossen + ist, erfolgt eine Überprüfung Ihres Infektionsstatus über eine vom RKI + eingerichtete Verifikations-Hotline. Das Auslösen einer Warnung über die + App auf der Grundlage einer bloßen Risiko-Mitteilung ist nicht zulässig. +

    +

    + Bei Unsicherheit: erst Hausarzt oder Gesundheitsamt kontaktieren +

    +

    + Wenn Sie nicht sicher sind, ob Sie sich infiziert haben oder nicht, dann + kontaktieren Sie bitte Ihren Hausarzt oder das zuständige Gesundheitsamt, + bevor Sie eine Warnung auslösen. Hier erhalten Sie weitere Beratung und + können sich erforderlichenfalls auf eine Infektion testen lassen. In der + Zwischenzeit befolgen Sie die allgemein gültigen Empfehlungen für das + Verhalten bei dem Verdacht einer SARS-CoV-2-Infektion. +

    +

    + Missbrauchsverbot +

    +

    + + Das missbräuchliche Auslösen einer Warnung ist untersagt und kann + schwerwiegende Konsequenzen nach sich ziehen. + + Insbesondere können Sie sich eventuell gegenüber anderen betroffenen + Personen schadenersatzpflichtig machen. +

    +

    + Das RKI behält sich vor, Sie bei Feststellung eines missbräuchlichen + Verhaltens von der weiteren Nutzung der App und der CWA-Dienste + auszuschließen. +

    +

    + + 7. Voraussetzungen für die Nutzung der App +

    +

    + Folgende technische Voraussetzungen sind für die Nutzung der App + erforderlich: +

    +

    + Sie benötigen eine Datenverbindung +

    +

    + Bestimmte Funktionen der App setzen auf zentrale Dienste und Systeme auf, + die über die CWA-Dienste zur Verfügung gestellt werden. Diese Funktionen + stehen daher nur zur Verfügung, wenn Ihr Endgerät über eine Datenverbindung + mit dem Internet verfügt, z.B. über UMTS, LTE oder WLAN, um hierüber auf + die CWA-Dienste zugreifen zu können. Ohne Datenverbindung stehen einige + oder alle Funktionen der App nicht zur Verfügung. Dies gilt auch, wenn Sie + Ihr Endgerät in den Flugmodus versetzen oder ausschalten. +

    +

    + Die App muss auf dem Endgerät laufen und eingeschaltet sein +

    +

    + Die App muss auf Ihrem Endgerät im Vorder- oder Hintergrund laufen und + eingeschaltet sein. Hierzu müssen Sie die App starten. Wenn Sie die App + nicht starten, ausschalten oder beenden, speichert die App keine + Begegnungen mit anderen Personen und erzeugt keine Zufallscodes zur + Speicherung durch andere Personen. Wenn Sie das Endgerät neu starten (z.B. + nach dem Ausschalten, nachdem die Batterie leer war oder nach einem Update + des Betriebssystems), müssen Sie auch die App neu starten. +

    +

    + Einstellungen im Endgerät +

    +

    + Für die Nutzung der App müssen Sie ferner die Bluetooth (BLE)-Funktionen + auf Ihrem Endgerät aktivieren und ggf. zur Verwendung durch die App + freigegeben. +

    +

    + Für die Nutzung der App empfiehlt das RKI ferner folgende Funktionen auf + Ihrem Endgerät zu aktivieren und ggf. zur Verwendung durch die App + freizugegeben, auch wenn diese nicht Voraussetzung für die Nutzung der + grundlegenden Funktionen der App sind: +

    +
      +
    • + Benachrichtigungen +
    • +
    • + Kamerafunktion +
    • +
    +

    + Bitte prüfen Sie in den Einstellungen ihres Endgeräts, ob diese Funktionen + aktiviert und für die Verwendung der App freigegeben sind. +

    +

    + Eine ausführliche Anleitung zur Einrichtung der App unter iOS und Android + finden Sie unter [Link]. Die Anleitung dient lediglich der + Erläuterung und ist nicht Teil dieser Nutzungsbedingungen. +

    +

    + Sie müssen immer die aktuelle Version der App verwenden +

    +

    + Das RKI wird von Zeit zu Zeit Updates der App zur Verfügung stellen. Sie + müssen diese Updates unverzüglich installieren und immer die neueste + verfügbare Version der App verwenden. Beim Verwenden älterer Versionen kann + es zu Fehlfunktionen und Störungen kommen. +

    +

    + + Sie benötigen aktuelle Versionen von iOS bzw. Android + +

    +

    + Die App verwendet eigens von Apple in iOS und Google in Android + implementierte Funktionen (sog. Exposure Notification). Diese + stehen in iOS erst ab Version 13.5 und in Android erst ab Version + [x] zur Verfügung. Die App funktioniert + daher leider nicht mit früheren Versionen der beiden Betriebssysteme. +

    +

    + + 8. Grenzen der App +

    +

    + Die App kann Sie dabei unterstützen, Risiko-Begegnungen mit Personen zu + erkennen, die später positiv getestet wurden. Die App hat aber auch + Grenzen, die berücksichtigt werden müssen. +

    +

    + Eine dieser Grenzen ist, dass die App zwar laufend eigene Zufallscodes ( Rolling Proximity + Identifiers) aussendet, aber andere Zufallscodes + nur in bestimmten Intervallen empfängt. Diese Empfangsfenster liegen + derzeit noch bis zu fünf Minuten auseinander und sind nur als sehr kurz + spezifiziert. In der App sind die Empfangsfenster vier Sekunden lang. +

    +

    + Für die Entfernungsmessung wird die Dämpfung des Bluetooth-Signals + verwendet. Eine geringere Dämpfung bedeutet dabei grundsätzlich, dass das + andere Endgerät näher ist. Eine höhere Dämpfung kann entweder bedeuten, + dass das andere Endgerät weiter entfernt ist (also eine Entfernung von mehr + als zwei Metern) oder dass sich zwischen den beiden Endgeräten etwas + befindet, was das Signal blockiert. Das können Objekte wie eine Wand oder + eine Tasche, in der sich das Endgerät befindet, sein, aber genauso Personen + oder Tiere. +

    +

    + 9. Nutzungsrechte +

    +

    + Einfaches Nutzungsrecht +

    +

    + Hiermit wird Ihnen eine eingeschränkte, nicht ausschließliche, nicht + übertragbare, nicht unterlizenzierbare, widerrufliche Lizenz zur Nutzung + der App für Ihre persönlichen, nicht kommerziellen Zwecke gewährt. +

    +

    + Die Lizenz für die iOS-Version der App umfasst die Nutzung auf jedem + Apple-Gerät, an dem Sie Eigentum haben oder über das Sie Kontrolle ausüben, + im Rahmen der geltenden Nutzungsbedingungen des App Stores, wobei auch über + andere Apple Accounts, die mit Ihrem Apple Account via Family Sharing oder + Volumenkauf verbunden sind, auf die App zugegriffen und diese genutzt + werden kann. +

    +

    + Sie dürfen im Rahmen von Datensicherungen Kopien der App anfertigen. + Darüberhinausgehende Rechte an der App werden Ihnen nicht eingeräumt. +

    +

    + Eigentum an der App +

    +

    + Die App (einschließlich des Quellcodes) ist das alleinige und + ausschließliche Eigentum von SAP SE, Dietmar Hopp Allee 16, 69190 Walldorf + („SAP“) oder deren Lizenzgebern, vorbehaltlich der Ihnen + nach Ziffer 9 eingeräumten Rechte sowie sonstiger Rechte an der App, die + SAP der Bundesrepublik Deutschland vertraglich eingeräumt hat. +

    +

    + Open Source-Lizenzhinweise +

    +

    + Information über die Verwendung von Drittkomponenten in der App und die + anwendbaren Lizenzbestimmungen finden Sie in den Open + Source-Lizenzhinweisen in der App. +

    +

    + Der Quellcode der App ist unter den Lizenzbedingungen der "Apache 2.0 + License" veröffentlicht und kann unter " + https://github.com/corona-warn-app + " heruntergeladen werden. Die Lizenzbedingungen der "Apache 2.0 License" + sind unter " + https://www.apache.org/licenses/LICENSE-2.0 + " abrufbar. +

    +

    + Die auf den Quellcode der App sowie auf in der App enthaltene + Drittkomponenten jeweils anwendbaren Lizenzbestimmungen bleiben von der + Rechteeinräumung nach dieser Ziffer 9 unberührt. +

    +

    + Was nicht erlaubt ist +

    +

    + Sie dürfen die App nicht manipulieren oder verändern. +

    +

    + Sie dürfen die App und die Schnittstellen zu den CWA-Diensten nicht + missbräuchlich verwenden. Sie dürfen die CWA-Dienste nicht für andere + Zwecke nutzen als den bestimmungsgemäßen Betrieb der App auf Ihrem + Endgerät. Sie dürfen auf die CWA-Dienste ausschließlich über die App + zugreifen. +

    +

    + + + 10. Wichtige Hinweise zu Verfügbarkeit und Änderungen + +

    +

    + Kein Anspruch auf bestimmte Funktionalität +

    +

    + Die App wird Ihnen so zur Verfügung gestellt, wie sie vom RKI herausgegeben + wird. Das RKI übernimmt keine Gewähr für die Funktionsfähigkeit der App + oder der CWA-Dienste und vereinbart mit Ihnen keine bestimmte + Beschaffenheit. Sie haben keinen Anspruch auf eine bestimmte Funktionalität + oder anderweitige Beschaffenheit der App oder der CWA-Dienste. Das RKI kann + die App jederzeit ändern und Funktionen ganz oder teilweise entfernen oder + die App um zusätzliche Funktionen erweitern. +

    +

    + Keine Verfügbarkeitszusage +

    +

    + Das RKI kann den Betrieb der App und der CWA-Dienste jederzeit einschränken + oder einstellen. Es besteht Ihrerseits kein Anspruch auf die weitergehende + Verfügbarkeit der App oder der damit verbundenen Dienste und Systeme + einschließlich der CWA-Dienste, weder in Bezug auf einzelne Funktionen noch + in Bezug auf das System als Ganzes. +

    +

    + Das RKI macht keine Zusagen über die Verfügbarkeit oder Leistungsfähigkeit + der CWA-Dienste. Diese können aufgrund von Wartungsarbeiten oder Störungen + vorübergehend und ggf. auch für längere Zeiträume nicht zur Verfügung + stehen. In diesen Fällen ist die Funktionalität der App ganz oder teilweise + eingeschränkt. +

    +

    + Änderung der Nutzungsbedingungen +

    +

    + Das RKI behält sich vor, diese Nutzungsbedingungen zu ändern. In diesem + Fall werden Sie beim Start der App aufgefordert, den geänderten + Nutzungsbedingungen zuzustimmen. Wenn Sie den geänderten + Nutzungsbedingungen nicht zustimmen, können Sie die App und die CWA- + Dienste nicht mehr nutzen und müssen die App von Ihrem Endgerät löschen. +

    +

    + Risk Score +

    +

    + Das RKI behält sich ferner vor, die im Rahmen der Risikobewertung ( risk score) + verwendeten Parameter jederzeit zu ändern, um dadurch + jeweils aktuellen Forschungsergebnissen zur Virusübertragung zu + entsprechen. Das RKI bestimmt die verwendeten Parameter jeweils nach seinem + Ermessen. +

    +

    + 11. Keine Gewährleistung +

    +

    + Das RKI bestimmt die Funktionen der App und der CWA-Dienste sowie deren + Ausgestaltung. Das RKI vereinbart mit Ihnen keine bestimmte Beschaffenheit + und Sie haben keinen Anspruch darauf, dass die App bestimmte Funktionen hat + oder diese in bestimmter Weise ausgestaltet sind. Die App wird in dem + Zustand und mit den Funktionen zur Verfügung gestellt, wie sie beim + Veröffentlichen der App im Apple App Store oder bei Google Play durch das + RKI implementiert sind. +

    +

    + Das RKI wird die App mit angemessener Sorgfalt zur Verfügung stellen und + angemessene Sorgfalt hinsichtlich der CWA-Dienste anwenden. Das RKI gibt + keine sonstigen Versprechen oder Zusicherungen im Hinblick auf die App oder + die CWA-Dienste ab und gewährleistet insbesondere nicht, dass: +

    +

    + · Ihre Nutzung der App oder der CWA-Dienste ohne Unterbrechung möglich oder + fehlerfrei sein wird, +

    +

    + · die App oder die CWA-Dienste frei von Verlusten, Korruption, Angriffen, + Viren, Eingriffen, Hacking oder anderen sicherheitsrelevanten Störungen + sein werden. +

    +

    + Für die Datensicherung Ihres Endgeräts sowie ggf. damit verbundener Systeme + sind Sie verantwortlich, inklusive der Datensicherung sämtlicher anderer + Apps, welche auf Ihrem Endgerät gespeichert sind. +

    +

    + + 12. Besondere Bedingungen für die iOS-Version der App + +

    +

    + Die folgenden Bedingungen gelten für den Bezug der App über den Apple App + Store und die Nutzung der App unter dem Betriebssystem iOS. +

    +

    + Einwilligung zur Nutzung von Daten +

    +

    + Sie erklären sich damit einverstanden, dass das RKI technische Daten und + zugehörige Informationen – insbesondere technische Informationen über Ihr + Gerät, System und Ihre Anwendungssoftware sowie Peripheriegeräte – erheben + und nutzen darf, die in regelmäßigen Abständen erfasst werden, um die + Bereitstellung von Software-Aktualisierungen, Produkt-Support und anderen + im Zusammenhang mit der App gegenüber Ihnen erbrachten Dienstleistungen + (soweit gegeben) zu erleichtern. Das RKI darf diese Informationen nutzen, + um seine Produkte zu verbessern oder Ihnen Dienstleistungen oder + Technologien zur Verfügung zu stellen, solange dies in einer Form erfolgt, + die Ihre Identität nicht preisgibt. +

    +

    + Wartung und Support +

    +

    + Das RKI ist als Herausgeber der App allein verantwortlich für Wartung und + Support der App nach Maßgabe dieser Nutzungsbedingungen. Apple übernimmt + keinerlei Verpflichtung zur Erbringung irgendwelcher Wartungs- und + Supportleistungen in Bezug auf die App. +

    +

    + Keine Haftung von Apple für Störungen +

    +

    + Im Fall von Störungen der App steht es Ihnen frei, Apple hierüber zu + informieren. Soweit gesetzlich zulässig hat Apple keine weitergehenden + Pflichten wegen Störungen der App. +

    +

    + Produkthaftung +

    +

    + Apple ist nicht verantwortlich für etwaige Ansprüche Ihrerseits oder von + Dritten in Bezug auf die App oder deren Besitz oder Verwendung + einschließlich +

    +
      +
    • + Ansprüchen wegen Produkthaftung, +
    • +
    +

    + · Ansprüchen auf der Basis, dass die App anwendbare gesetzliche oder + regulatorische Anforderungen nicht erfüllt und +

    +

    + · Ansprüchen unter Verbrauchschutz- und Datenschutzgesetzen oder ähnlichen + Gesetzen, +

    +

    + einschließlich im Zusammenhang mit der Nutzung der HealthKit und + HomeKit-Frameworks. +

    +

    + Verletzung von Schutzrechten Dritter +

    +

    + Für den Fall, dass Dritte Ansprüche wegen der Verletzung von Schutzrechten + durch die App oder den Besitz oder die Verwendung der App durch Sie geltend + machen, ist Apple nicht verantwortlich für Untersuchungen, Verteidigung, + Beilegung oder Erfüllung solcher Ansprüche wegen der Verletzung von + Schutzrechten. +

    +

    + US Embargos und Sanktionen +

    +

    + Mit Anerkennen dieser Nutzungsbedingungen bestätigten Sie, +

    +

    + · dass Sie sich nicht in einem Land aufhalten, das einem Embargo der + Regierung der Vereinigten Staaten von Amerika unterliegt oder von der + Regierung der Vereinigten Staaten von Amerika als Unterstützer von + Terroristen ("terrorist supporting" country) designiert wurde und +

    +

    + · dass Sie nicht auf einer Liste der Regierung der Vereinigten Staaten von + Amerika als sog. Prohibited or Restricted Party geführt werden. +

    +

    + Drittbegünstigung von Apple +

    +

    + Sie erkennen an und erklären sich damit einverstanden, dass Apple ein + Drittbegünstigter unter diesen Nutzungsbedingungen ist und Apple daher + diese Nutzungsbedingungen Ihnen gegenüber durchsetzen kann. Die Änderung + und Aufhebung dieser Nutzungsbedingungen einschließlich der Rechte von + Apple hierunter bleibt den Vertragsparteien vorbehalten und bedarf nicht + der Zustimmung von Apple. +

    +

    + 13. Schlussbestimmungen +

    +

    + Bestimmungsgemäße Nutzung / Sperrung bei missbräuchlicher Verwendung +

    +

    + Sie dürfen die App und die CWA-Dienste nur bestimmungsgemäß nutzen. Das RKI + behält sich vor, Sie bei missbräuchlicher Verwendung der App oder nicht + bestimmungsgemäßem Zugriff auf die CWA-Dienste von der Nutzung der App und + der CWA-Dienste auszuschließen. +

    +

    + Dienste Dritter +

    +

    + Sofern Sie im Zusammenhang mit der Nutzung der App Dienste Dritter in + Anspruch nehmen, insbesondere die Dienste von + Telekommunikationsdienstleistern für die Bereitstellung einer + Datenverbindung, sind Sie für damit im Zusammenhang stehenden Kosten und + die Einhaltung der jeweiligen Vertragsbedingungen selbst verantwortlich. +

    +

    + Anwendbares Recht +

    +

    + Diese Nutzungsbedingungen unterliegen dem Recht der Bundesrepublik + Deutschland. Das Übereinkommen der Vereinten Nationen über Verträge über + den internationalen Warenkauf findet keine Anwendung. Die gesetzlichen + Vorschriften zur Beschränkung der Rechtswahl und zur Anwendbarkeit + zwingender Vorschriften insbesondere des Staates, in dem Sie als + Verbraucher Ihren gewöhnlichen Aufenthalt haben, bleiben unberührt. +

    +

    + Teilunwirksamkeit +

    +

    + Diese Nutzungsbedingungen bleiben auch bei rechtlicher Unwirksamkeit + einzelner Punkte in ihren übrigen Teilen verbindlich. Anstelle der + unwirksamen Punkte treten, soweit vorhanden, die gesetzlichen Vorschriften. + Soweit dies für eine Vertragspartei eine unzumutbare Härte darstellen + würde, werden die Nutzungsbedingungen jedoch im Ganzen unwirksam. +

    \ No newline at end of file diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationPrivacyFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationPrivacyFragment.kt index bc5b552d1f6..52aa50a9966 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationPrivacyFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationPrivacyFragment.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentInformationPrivacyBinding import de.rki.coronawarnapp.ui.BaseFragment import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.AssetConstants /** * Basic Fragment which only displays static content. @@ -39,6 +40,7 @@ class InformationPrivacyFragment : BaseFragment() { super.onViewCreated(view, savedInstanceState) setButtonOnClickListener() setContentDescription() + loadWebView() } override fun onStart() { @@ -61,4 +63,9 @@ class InformationPrivacyFragment : BaseFragment() { (activity as MainActivity).goBack() } } + + private fun loadWebView() { + val informationPrivacyHtmlFilename = getString(R.string.information_privacy_html_path) + binding.informationPrivacyWebview.loadUrl(AssetConstants.ANDROID_ASSET_PATH + informationPrivacyHtmlFilename) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTechnicalFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTechnicalFragment.kt index 1c5da555f63..cc10f8386c2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTechnicalFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTechnicalFragment.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentInformationTechnicalBinding import de.rki.coronawarnapp.ui.BaseFragment import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.AssetConstants /** * Basic Fragment which only displays static content. @@ -39,6 +40,7 @@ class InformationTechnicalFragment : BaseFragment() { super.onViewCreated(view, savedInstanceState) setButtonOnClickListener() setContentDescription() + loadWebView() } override fun onStart() { @@ -61,4 +63,9 @@ class InformationTechnicalFragment : BaseFragment() { (activity as MainActivity).goBack() } } + + private fun loadWebView() { + val informationTermsHtmlFilename = getString(R.string.information_technical_html_path) + binding.informationTechnicalWebview.loadUrl(AssetConstants.ANDROID_ASSET_PATH + informationTermsHtmlFilename) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTermsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTermsFragment.kt index 60561121d14..afdd88484e3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTermsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationTermsFragment.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentInformationTermsBinding import de.rki.coronawarnapp.ui.BaseFragment import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.AssetConstants /** * Basic Fragment which only displays static content. @@ -39,6 +40,7 @@ class InformationTermsFragment : BaseFragment() { super.onViewCreated(view, savedInstanceState) setButtonOnClickListener() setContentDescription() + loadWebView() } override fun onStart() { @@ -61,4 +63,9 @@ class InformationTermsFragment : BaseFragment() { (activity as MainActivity).goBack() } } + + private fun loadWebView() { + val informationTermsHtmlFilename = getString(R.string.information_terms_html_path) + binding.informationTermsWebview.loadUrl(AssetConstants.ANDROID_ASSET_PATH + informationTermsHtmlFilename) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragment.kt index d0b72544881..cb6e600ab7f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyFragment.kt @@ -5,8 +5,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent +import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentOnboardingPrivacyBinding import de.rki.coronawarnapp.ui.BaseFragment +import de.rki.coronawarnapp.util.AssetConstants /** * This fragment informs the user regarding privacy. @@ -36,6 +38,7 @@ class OnboardingPrivacyFragment : BaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setButtonOnClickListener() + loadWebView() } override fun onStart() { @@ -58,4 +61,9 @@ class OnboardingPrivacyFragment : BaseFragment() { (activity as OnboardingActivity).goBack() } } + + private fun loadWebView() { + val informationPrivacyHtmlFilename = getString(R.string.information_privacy_html_path) + binding.onboardingPrivacyWebview.loadUrl(AssetConstants.ANDROID_ASSET_PATH + informationPrivacyHtmlFilename) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AssetConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AssetConstants.kt new file mode 100644 index 00000000000..e8cf30919dc --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AssetConstants.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.util + +object AssetConstants { + // Path to android assets + const val ANDROID_ASSET_PATH = "file:////android_asset/" +} diff --git a/Corona-Warn-App/src/main/res/layout/fragment_information_privacy.xml b/Corona-Warn-App/src/main/res/layout/fragment_information_privacy.xml index 261f2b1296f..bd660ccb202 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_information_privacy.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_information_privacy.xml @@ -38,7 +38,7 @@ + android:layout_height="match_parent"> - diff --git a/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml b/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml index a6f144879ef..46eb5b81757 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_information_technical.xml @@ -39,7 +39,7 @@ + android:layout_height="match_parent"> - diff --git a/Corona-Warn-App/src/main/res/layout/fragment_information_terms.xml b/Corona-Warn-App/src/main/res/layout/fragment_information_terms.xml index 8de7a6cd16c..47346e76445 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_information_terms.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_information_terms.xml @@ -38,7 +38,7 @@ + android:layout_height="match_parent"> - diff --git a/Corona-Warn-App/src/main/res/layout/fragment_onboarding_privacy.xml b/Corona-Warn-App/src/main/res/layout/fragment_onboarding_privacy.xml index d47812837e4..7d955f29464 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_onboarding_privacy.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_onboarding_privacy.xml @@ -44,20 +44,30 @@ - + app:layout_constraintTop_toBottomOf="@+id/onboarding_header" /> + + +