diff --git a/.circleci/config.yml b/.circleci/config.yml index 42172c9c65cfb4be3f97cc6af1170b860cf5ed8c..14bc29650720f507436df173231e23e8c89cb838 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -548,5 +548,9 @@ workflows: branches: ignore: /.*/ - device_screenshots: - requires: - - quick_build_device_for_testers_signed \ No newline at end of file + filters: + tags: + only: + - /^v.*/ + branches: + ignore: /.*/ \ No newline at end of file diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 3616d8b0a02c1a46a9c81045c3e965d7d684e502..c4e67dc43914002c015b94071e77d544b15fb80b 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -197,7 +197,7 @@ android { returnDefaultValues = true } - // Using orchestration toegether with mockk on x86 (32bit) emulator images crashes + // Using orchestration together with mockk on x86 (32bit) emulator images crashes // Leaving this in here as reminder // https://github.com/android/android-test/issues/352 // https://github.com/mockk/mockk/issues/466 @@ -319,7 +319,7 @@ dependencies { implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc03" // DAGGER - def dagger_version = "2.30.1" + def dagger_version = "2.31.2" implementation "com.google.dagger:dagger:$dagger_version" implementation "com.google.dagger:dagger-android:$dagger_version" implementation "com.google.dagger:dagger-android-support:$dagger_version" @@ -330,10 +330,6 @@ dependencies { kaptTest "com.google.dagger:dagger-android-processor:$dagger_version" kaptAndroidTest "com.google.dagger:dagger-android-processor:$dagger_version" - def assisted_injection_version = "0.6.0" - compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:$assisted_injection_version" - kapt "com.squareup.inject:assisted-inject-processor-dagger2:$assisted_injection_version" - // QR implementation('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false } // noinspection GradleDependency - needed for SDK 23 compatibility, in combination with com.journeyapps:zxing-android-embedded:4.1.0 diff --git a/Corona-Warn-App/schemas/de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase/3.json b/Corona-Warn-App/schemas/de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase/3.json new file mode 100644 index 0000000000000000000000000000000000000000..afe5ea80dc406ade2d451dfa1a09e7566e5e3e3f --- /dev/null +++ b/Corona-Warn-App/schemas/de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase/3.json @@ -0,0 +1,247 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "42caa51e3dfceb800f45aae144eeb39f", + "entities": [ + { + "tableName": "riskresults", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`monotonicId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `calculatedAt` TEXT NOT NULL, `failureReason` TEXT, `totalRiskLevel` INTEGER, `totalMinimumDistinctEncountersWithLowRisk` INTEGER, `totalMinimumDistinctEncountersWithHighRisk` INTEGER, `mostRecentDateWithLowRisk` TEXT, `mostRecentDateWithHighRisk` TEXT, `numberOfDaysWithLowRisk` INTEGER, `numberOfDaysWithHighRisk` INTEGER)", + "fields": [ + { + "fieldPath": "monotonicId", + "columnName": "monotonicId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "calculatedAt", + "columnName": "calculatedAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "failureReason", + "columnName": "failureReason", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.totalRiskLevel", + "columnName": "totalRiskLevel", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.totalMinimumDistinctEncountersWithLowRisk", + "columnName": "totalMinimumDistinctEncountersWithLowRisk", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.totalMinimumDistinctEncountersWithHighRisk", + "columnName": "totalMinimumDistinctEncountersWithHighRisk", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.mostRecentDateWithLowRisk", + "columnName": "mostRecentDateWithLowRisk", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.mostRecentDateWithHighRisk", + "columnName": "mostRecentDateWithHighRisk", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.numberOfDaysWithLowRisk", + "columnName": "numberOfDaysWithLowRisk", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "aggregatedRiskResult.numberOfDaysWithHighRisk", + "columnName": "numberOfDaysWithHighRisk", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "monotonicId" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "exposurewindows", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `riskLevelResultId` TEXT NOT NULL, `dateMillisSinceEpoch` INTEGER NOT NULL, `calibrationConfidence` INTEGER NOT NULL, `infectiousness` INTEGER NOT NULL, `reportType` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "riskLevelResultId", + "columnName": "riskLevelResultId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dateMillisSinceEpoch", + "columnName": "dateMillisSinceEpoch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "calibrationConfidence", + "columnName": "calibrationConfidence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "infectiousness", + "columnName": "infectiousness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reportType", + "columnName": "reportType", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "scaninstances", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `exposureWindowId` INTEGER NOT NULL, `minAttenuationDb` INTEGER NOT NULL, `secondsSinceLastScan` INTEGER NOT NULL, `typicalAttenuationDb` INTEGER NOT NULL, FOREIGN KEY(`exposureWindowId`) REFERENCES `exposurewindows`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exposureWindowId", + "columnName": "exposureWindowId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minAttenuationDb", + "columnName": "minAttenuationDb", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "secondsSinceLastScan", + "columnName": "secondsSinceLastScan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typicalAttenuationDb", + "columnName": "typicalAttenuationDb", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_scaninstances_exposureWindowId", + "unique": false, + "columnNames": [ + "exposureWindowId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_scaninstances_exposureWindowId` ON `${TABLE_NAME}` (`exposureWindowId`)" + } + ], + "foreignKeys": [ + { + "table": "exposurewindows", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "exposureWindowId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "riskperdate", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dateMillisSinceEpoch` INTEGER NOT NULL, `riskLevel` INTEGER NOT NULL, `minimumDistinctEncountersWithLowRisk` INTEGER NOT NULL, `minimumDistinctEncountersWithHighRisk` INTEGER NOT NULL, PRIMARY KEY(`dateMillisSinceEpoch`))", + "fields": [ + { + "fieldPath": "dateMillisSinceEpoch", + "columnName": "dateMillisSinceEpoch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "riskLevel", + "columnName": "riskLevel", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minimumDistinctEncountersWithLowRisk", + "columnName": "minimumDistinctEncountersWithLowRisk", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minimumDistinctEncountersWithHighRisk", + "columnName": "minimumDistinctEncountersWithHighRisk", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "dateMillisSinceEpoch" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '42caa51e3dfceb800f45aae144eeb39f')" + ] + } +} \ No newline at end of file diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt index af16ce9d74ebb2c11e86b1689e934988951ab379..730112da5305024e7af29610eceb27fdbdefadb6 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt @@ -1,5 +1,7 @@ package de.rki.coronawarnapp.risk.storage +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.core.app.ApplicationProvider @@ -8,9 +10,11 @@ import androidx.test.platform.app.InstrumentationRegistry import de.rki.coronawarnapp.risk.RiskLevelResult import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration1To2 +import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration2To3 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.joda.time.Instant @@ -19,6 +23,7 @@ import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseTest import timber.log.Timber +import java.io.IOException @RunWith(AndroidJUnit4::class) class RiskResultDatabaseMigrationTest : BaseTest() { @@ -221,4 +226,84 @@ class RiskResultDatabaseMigrationTest : BaseTest() { Timber.v("insertedResult=%s", insertedResult) insertedResult shouldBe expectedResult.copy(monotonicId = 1) } + + @Test + @Throws(IOException::class) + fun migrate2to3() { + val riskLevelValues = ContentValues().apply { + put("monotonicId", 1337L) + put("id", "72c4084a-43a9-4fcf-86d4-36103bfbd492") + put("calculatedAt", "2020-12-31T16:41:50.207Z") + put("totalRiskLevel", 2) + put("totalMinimumDistinctEncountersWithLowRisk", 8) + put("totalMinimumDistinctEncountersWithHighRisk", 1) + put("mostRecentDateWithLowRisk", "2020-12-29T16:41:50.038Z") + put("mostRecentDateWithHighRisk", "2020-12-30T16:41:50.038Z") + put("numberOfDaysWithLowRisk", 3) + put("numberOfDaysWithHighRisk", 1) + } + + helper.createDatabase(DB_NAME, 2).apply { + insert("riskresults", SQLiteDatabase.CONFLICT_ABORT, riskLevelValues) + close() + } + + val values = ContentValues().apply { + put("dateMillisSinceEpoch", Instant.parse("2020-12-31T16:28:25.400Z").millis) + put("riskLevel", 1) + put("minimumDistinctEncountersWithLowRisk", 0) + put("minimumDistinctEncountersWithHighRisk", 0) + } + + // Run migration from 2 to 3 + helper.runMigrationsAndValidate(DB_NAME, 3, true, RiskResultDatabaseMigration2To3).apply { + insert("riskperdate", SQLiteDatabase.CONFLICT_REPLACE, values) + } + + val db = RiskResultDatabase.Factory( + context = ApplicationProvider.getApplicationContext() + ).create(databaseName = DB_NAME) + + runBlocking { + // Check AggregatedRiskPerDateResult + val result = db.aggregatedRiskPerDate().allEntries().first().first() + result.dateMillisSinceEpoch shouldBe values["dateMillisSinceEpoch"] + result.riskLevel shouldBe RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.forNumber( + values["riskLevel"] as Int + ) + result.minimumDistinctEncountersWithLowRisk shouldBe values["minimumDistinctEncountersWithLowRisk"] + result.minimumDistinctEncountersWithHighRisk shouldBe values["minimumDistinctEncountersWithHighRisk"] + + // Check RiskLevel + val riskLevel = db.riskResults().allEntries().first().first() + riskLevel.monotonicId shouldBe riskLevelValues["monotonicId"] + riskLevel.id shouldBe riskLevelValues["id"] + riskLevel.calculatedAt shouldBe Instant.parse(riskLevelValues["calculatedAt"] as String) + riskLevel.aggregatedRiskResult shouldNotBe null + riskLevel.aggregatedRiskResult?.totalRiskLevel shouldBe RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.forNumber(riskLevelValues["totalRiskLevel"] as Int) + riskLevel.aggregatedRiskResult?.totalMinimumDistinctEncountersWithLowRisk shouldBe riskLevelValues["totalMinimumDistinctEncountersWithLowRisk"] + riskLevel.aggregatedRiskResult?.totalMinimumDistinctEncountersWithHighRisk shouldBe riskLevelValues["totalMinimumDistinctEncountersWithHighRisk"] + riskLevel.aggregatedRiskResult?.mostRecentDateWithLowRisk shouldBe Instant.parse(riskLevelValues["mostRecentDateWithLowRisk"] as String) + riskLevel.aggregatedRiskResult?.mostRecentDateWithHighRisk shouldBe Instant.parse(riskLevelValues["mostRecentDateWithHighRisk"] as String) + riskLevel.aggregatedRiskResult?.numberOfDaysWithLowRisk shouldBe riskLevelValues["numberOfDaysWithLowRisk"] + riskLevel.aggregatedRiskResult?.numberOfDaysWithHighRisk shouldBe riskLevelValues["numberOfDaysWithHighRisk"] + } + } + + @Test + @Throws(IOException::class) + fun migrateAll() { + helper.createDatabase(DB_NAME, 1).apply { + close() + } + + // Open latest version of the database. Room will validate the schema + // once all migrations execute. + RiskResultDatabase.Factory( + context = ApplicationProvider.getApplicationContext() + ).create(databaseName = DB_NAME).apply { + openHelper.writableDatabase + close() + } + } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt index 8dffeef996f4c1efbdc79e9e4a3f1018b5b90685..3c3b2446dd1eb2337b6da46b335e2469f25c77cc 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt @@ -4,12 +4,15 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import de.rki.coronawarnapp.risk.RiskLevelResult import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase +import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.joda.time.Instant +import org.joda.time.LocalDate import org.junit.Test import org.junit.runner.RunWith import java.util.UUID @@ -143,4 +146,49 @@ class RiskResultDatabaseTest { latestAndLastSuccessful().first() shouldBe listOf(newestEntryFailed, oldestSuccessfulEntry) } } + + @Test + fun aggregatedRiskPerDate_CRUD(): Unit = runBlocking { + database.clearAllTables() + + val date = LocalDate.parse("2021-01-21") + + val firstResult = PersistedAggregatedRiskPerDateResult( + dateMillisSinceEpoch = date.toDateTimeAtStartOfDay().millis, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW, + minimumDistinctEncountersWithLowRisk = 10, + minimumDistinctEncountersWithHighRisk = 0 + ) + + val secondResult = PersistedAggregatedRiskPerDateResult( + dateMillisSinceEpoch = date.minusDays(5).toDateTimeAtStartOfDay().millis, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW, + minimumDistinctEncountersWithLowRisk = 10, + minimumDistinctEncountersWithHighRisk = 0 + ) + + val replaceSecondResult = secondResult.copy( + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + minimumDistinctEncountersWithHighRisk = 20 + ) + + database.aggregatedRiskPerDate().apply { + // Proof we are not cheating + allEntries().first() shouldBe emptyList() + + // Check insert + insertRisk(listOf(firstResult, secondResult)) + allEntries().first() shouldBe listOf(firstResult, secondResult) + + // Check replace + insertRisk(listOf(replaceSecondResult)) + val entries = allEntries().first() + entries shouldNotBe listOf(firstResult, secondResult) + entries shouldBe listOf(firstResult, replaceSecondResult) + + // Check delete + delete(listOf(replaceSecondResult)) + allEntries().first() shouldBe listOf(firstResult) + } + } } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt index 0cbbed399eb73d7d1df17380e4bf13d0b2d42283..08e05bb2ad77fcc99cfb954d678c0f1190aba543 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt @@ -124,14 +124,14 @@ class ContactDiaryDayFragmentTest : BaseUITest() { private fun setupViewModels() { viewModel = spyk( ContactDiaryDayViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), selectedDay ) ) personListViewModel = spyk( ContactDiaryPersonListViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), selectedDay, contactDiaryRepository ) @@ -139,7 +139,7 @@ class ContactDiaryDayFragmentTest : BaseUITest() { locationListViewModel = spyk( ContactDiaryLocationListViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), selectedDay, contactDiaryRepository ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt index 3bdfbe3449baf012e34e691b379fdf332046e4e8..0b8e1dacfdb8eef77fdeeae8bb451477c7806122 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt @@ -46,7 +46,7 @@ class ContactDiaryEditLocationsFragmentTest : BaseUITest() { viewModel = spyk( ContactDiaryEditLocationsViewModel( contactDiaryRepository, - TestDispatcherProvider + TestDispatcherProvider() ) ) setupMockViewModel( diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt index 91b359f039c8ff905ac4a05bcf05b7b66ec90c28..00cb0d90e12a0e545b44f228aee8d0377a628c6a 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt @@ -46,7 +46,7 @@ class ContactDiaryEditPersonsFragmentTest : BaseUITest() { viewModel = spyk( ContactDiaryEditPersonsViewModel( contactDiaryRepository, - TestDispatcherProvider + TestDispatcherProvider() ) ) setupMockViewModel( diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryOverviewFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryOverviewFragmentTest.kt index 18b7685674dff75d244a1effe35b83d59e5a10a9..2b0e52b5537814e61fd0aa6aede53ebf9a38ffc7 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryOverviewFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryOverviewFragmentTest.kt @@ -5,12 +5,16 @@ import androidx.lifecycle.MutableLiveData import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragment import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewViewModel import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ListItem +import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.task.TaskController -import de.rki.coronawarnapp.ui.contactdiary.DiaryData.LIST_ITEMS +import de.rki.coronawarnapp.ui.contactdiary.DiaryData.DATA_ITEMS +import de.rki.coronawarnapp.ui.contactdiary.DiaryData.HIGH_RISK +import de.rki.coronawarnapp.ui.contactdiary.DiaryData.LOW_RISK import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -42,6 +46,7 @@ class ContactDiaryOverviewFragmentTest : BaseUITest() { @MockK lateinit var taskController: TaskController @MockK lateinit var contactDiaryRepository: ContactDiaryRepository + @MockK lateinit var riskLevelStorage: RiskLevelStorage private lateinit var viewModel: ContactDiaryOverviewViewModel @@ -51,8 +56,9 @@ class ContactDiaryOverviewFragmentTest : BaseUITest() { viewModel = spyk( ContactDiaryOverviewViewModel( taskController = taskController, - dispatcherProvider = TestDispatcherProvider, - contactDiaryRepository = contactDiaryRepository + dispatcherProvider = TestDispatcherProvider(), + contactDiaryRepository = contactDiaryRepository, + riskLevelStorage = riskLevelStorage ) ) @@ -88,7 +94,8 @@ class ContactDiaryOverviewFragmentTest : BaseUITest() { .map { LocalDate.now().minusDays(it) } .map { ListItem(it).apply { - data.apply { addAll(LIST_ITEMS) } + data.addAll(DATA_ITEMS) + risk = if (it.dayOfYear % 2 == 0) HIGH_RISK else LOW_RISK } } ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt index 910abff2693ecc7e8a709d9b9d2fe60101b09484..c6b8e97ffd73055f414903dfca305524848b04c4 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt @@ -11,7 +11,7 @@ import de.rki.coronawarnapp.util.ui.toLazyString object DiaryData { - val LIST_ITEMS = listOf( + val DATA_ITEMS = listOf( ListItem.Data( R.drawable.ic_contact_diary_person_item, "Max Mustermann", @@ -37,6 +37,18 @@ object DiaryData { ) ) + val HIGH_RISK = ListItem.Risk( + R.string.contact_diary_risk_body, + R.string.contact_diary_high_risk_title, + R.drawable.ic_high_risk_alert + ) + + val LOW_RISK = ListItem.Risk( + R.string.contact_diary_risk_body, + R.string.contact_diary_low_risk_title, + R.drawable.ic_low_risk_alert + ) + val LOCATIONS: List<SelectableItem<ContactDiaryLocation>> = listOf( SelectableItem( selected = true, diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt index 6bc5240354067ae20e685e1d42ba7f10d7329ca0..7bd75328019015c8a150ad170063c68f65e13d82 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt @@ -247,7 +247,7 @@ class HomeFragmentTest : BaseUITest() { private fun homeFragmentViewModelSpy() = spyk( HomeFragmentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), errorResetTool = errorResetTool, tracingRepository = tracingRepository, tracingStateProviderFactory = tracingStateProviderFactory, diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt index 829ed1551d57246345e1a7f7457f7be0762e36bf..5a3922923f8b32e1785e6ab04ec6c56fabb4d3f0 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentTest.kt @@ -13,13 +13,13 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import testhelpers.BaseUITest +import testhelpers.SCREENSHOT_DELAY_TIME import testhelpers.Screenshot import testhelpers.SystemUIDemoModeRule import testhelpers.TestDispatcherProvider import testhelpers.launchFragmentInContainer2 import tools.fastlane.screengrab.Screengrab import tools.fastlane.screengrab.locale.LocaleTestRule -import testhelpers.SCREENSHOT_DELAY_TIME @RunWith(AndroidJUnit4::class) class OnboardingDeltaInteroperabilityFragmentTest : BaseUITest() { @@ -41,7 +41,7 @@ class OnboardingDeltaInteroperabilityFragmentTest : BaseUITest() { override fun create(): OnboardingDeltaInteroperabilityFragmentViewModel = OnboardingDeltaInteroperabilityFragmentViewModel( interopRepo = interopRepo, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) }) } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt index a6be434831ba9b3312b5472a91ea776984e8cff9..2322ccc008e70984cd7e661b3b72526a37b53b84 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentTest.kt @@ -48,7 +48,7 @@ class OnboardingTracingFragmentTest : BaseUITest() { OnboardingTracingFragmentViewModel( interoperabilityRepository = interopRepo, tracingPermissionHelperFactory = factory, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt index aa8dbed36474a8b139559f3aaaf18029c27e38fb..eacde5485310325a6cef7dc57e5281f14eb0cb20 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt @@ -42,7 +42,8 @@ class SubmissionConsentFragmentTest : BaseUITest() { fun setup() { MockKAnnotations.init(this, relaxed = true) every { interoperabilityRepository.countryList } returns flowOf() - viewModel = SubmissionConsentViewModel(submissionRepository, interoperabilityRepository, TestDispatcherProvider) + viewModel = + SubmissionConsentViewModel(submissionRepository, interoperabilityRepository, TestDispatcherProvider()) setupMockViewModel(object : SubmissionConsentViewModel.Factory { override fun create(): SubmissionConsentViewModel = viewModel }) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt index eaa2c28964f58478f8bc5b2290cf24bf2ac6f95e..2fc4006c280e2badf61984db0df2613ff33dd788 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt @@ -7,6 +7,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R @@ -68,7 +69,7 @@ class SubmissionContactFragmentTest : BaseUITest() { @Test fun testContactEnterTanClicked() { val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) - navController.setGraph(R.navigation.nav_graph) + runOnUiThread { navController.setGraph(R.navigation.nav_graph) } launchFragmentInContainer2<SubmissionContactFragment>().onFragment { fragment -> Navigation.setViewNavController(fragment.requireView(), navController) } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt index e47846666f814ffe11fce9942ef33bb864844ea9..cc5d49e1f0335b7e0c32420c82a71d2236d753cc 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt @@ -8,6 +8,7 @@ import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R @@ -41,8 +42,10 @@ class SubmissionDispatcherFragmentTest : BaseUITest() { private fun createViewModel() = SubmissionDispatcherViewModel() - private val navController = TestNavHostController(ApplicationProvider.getApplicationContext()).apply { - setGraph(R.navigation.nav_graph) + private val navController = TestNavHostController( + ApplicationProvider.getApplicationContext() + ).apply { + runOnUiThread { setGraph(R.navigation.nav_graph) } } @Before diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt index a2ad56bdd115aaae9624bd742d6ab2b67ad67952..411960fcb5a713551242b124634a74e9180a2a9a 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt @@ -53,7 +53,7 @@ class SubmissionSymptomCalendarFragmentTest : BaseUITest() { viewModel = spyk( SubmissionSymptomCalendarViewModel( Symptoms.Indication.POSITIVE, - TestDispatcherProvider, + TestDispatcherProvider(), submissionRepository, autoSubmission ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt index 4b8224920c6278fcfd1c878b9883e16bd7911a15..abd98246044e9e3edcd05b2a18e0e54bfe6ce391 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt @@ -53,7 +53,7 @@ class SubmissionSymptomIntroFragmentTest : BaseUITest() { fun setup() { MockKAnnotations.init(this, relaxed = true) viewModel = - spyk(SubmissionSymptomIntroductionViewModel(TestDispatcherProvider, submissionRepository, autoSubmission)) + spyk(SubmissionSymptomIntroductionViewModel(TestDispatcherProvider(), submissionRepository, autoSubmission)) with(viewModel) { every { symptomIndication } returns MutableLiveData(Symptoms.Indication.POSITIVE) } @@ -76,9 +76,7 @@ class SubmissionSymptomIntroFragmentTest : BaseUITest() { fun testSymptomNextClicked() { val scenario = launchFragmentInContainer<SubmissionSymptomIntroductionFragment>() onView(withId(R.id.symptom_button_next)) - .perform(scrollTo()) .perform(click()) - // TODO verify result } @@ -86,7 +84,7 @@ class SubmissionSymptomIntroFragmentTest : BaseUITest() { @Screenshot fun capture_fragment() { captureScreenshot<SubmissionSymptomIntroductionFragment>() - onView(withId(R.id.symptom_button_next)) + onView(withId(R.id.target_button_verify)) .perform(scrollTo()) Thread.sleep(SCREENSHOT_DELAY_TIME) Screengrab.screenshot(SubmissionSymptomIntroductionFragment::class.simpleName.plus("2")) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt index 38331cf9e29415436dce1770cb6911832ebc6254..8cf2b0f6911882a112d629830d9ca4400d073c28 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt @@ -36,7 +36,7 @@ class SubmissionTanFragmentTest : BaseUITest() { @MockK lateinit var submissionRepository: SubmissionRepository private fun createViewModel() = SubmissionTanViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), submissionRepository = submissionRepository ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt index b0370de7b7dba3d4e1d61130911b146fe23b5ee9..9269b89a1c753ba71101c333e998bc80b8fa5088 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt @@ -6,7 +6,7 @@ import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.auto.AutoSubmission -import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryUpdater_AssistedFactory +import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryUpdater_Factory_Impl import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableFragment import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableViewModel import io.mockk.MockKAnnotations @@ -31,7 +31,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() { lateinit var viewModel: SubmissionTestResultAvailableViewModel @MockK lateinit var submissionRepository: SubmissionRepository - @MockK lateinit var tekHistoryUpdaterFactory: TEKHistoryUpdater_AssistedFactory + @MockK lateinit var tekHistoryUpdaterFactory: TEKHistoryUpdater_Factory_Impl @MockK lateinit var autoSubmission: AutoSubmission @Rule @@ -50,7 +50,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() { viewModel = spyk( SubmissionTestResultAvailableViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), tekHistoryUpdaterFactory, submissionRepository, autoSubmission diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt index 2d1ef2e0cd4a262b7de16e7355fc23fe06de8b06..506cd23ebfcb88162888b233e00ccea81055eaa2 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt @@ -9,6 +9,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R @@ -54,8 +55,10 @@ class SubmissionTestResultConsentGivenFragmentTest : BaseUITest() { private lateinit var viewModel: SubmissionTestResultConsentGivenViewModel - private val navController = TestNavHostController(ApplicationProvider.getApplicationContext()).apply { - setGraph(R.navigation.nav_graph) + private val navController = TestNavHostController( + ApplicationProvider.getApplicationContext() + ).apply { + runOnUiThread { setGraph(R.navigation.nav_graph) } } @Before @@ -67,7 +70,7 @@ class SubmissionTestResultConsentGivenFragmentTest : BaseUITest() { submissionRepository, autoSubmission, testResultAvailableNotificationService, - TestDispatcherProvider + TestDispatcherProvider() ) ) setupMockViewModel( diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt index 7305ee020dd89a18ecb465e62907b53a11006b17..781c3f544c6448ce84b1a713240c0bb193bf5cbc 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt @@ -60,7 +60,7 @@ class SubmissionTestResultFragmentTest : BaseUITest() { viewModel = spyk( SubmissionTestResultPendingViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), shareTestResultNotificationService, submissionRepository ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt index a10ad9e78408bf348114b42a58aa5847bf6f717f..d99b017e1e9254579e75da02983519ae1f6ae612 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt @@ -52,7 +52,7 @@ class SubmissionTestResultNegativeFragmentTest : BaseUITest() { viewModel = spyk( SubmissionTestResultNegativeViewModel( - TestDispatcherProvider, + TestDispatcherProvider(), submissionRepository, testResultAvailableNotificationService ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionYourConsentFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionYourConsentFragmentTest.kt index 909eb1c5d30e9ee41d79d828e83d7d0a0ff8894a..1312ce621eb0ef494eb4ad9ed790ac3b7ec4dc7b 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionYourConsentFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionYourConsentFragmentTest.kt @@ -44,7 +44,7 @@ class SubmissionYourConsentFragmentTest : BaseUITest() { MockKAnnotations.init(this, relaxed = true) every { submissionRepository.hasGivenConsentToSubmission } returns flowOf() viewModel = - SubmissionYourConsentViewModel(TestDispatcherProvider, interoperabilityRepository, submissionRepository) + SubmissionYourConsentViewModel(TestDispatcherProvider(), interoperabilityRepository, submissionRepository) setupMockViewModel(object : SubmissionYourConsentViewModel.Factory { override fun create(): SubmissionYourConsentViewModel = viewModel }) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt index e221717278a169fd8809a12c291aa0ccaee0a3b1..617a7cc2f5d8418b24cb8e0b1c3dc8b2b76c05a8 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt @@ -58,7 +58,7 @@ class TracingDetailsFragmentTest : BaseUITest() { viewModel = spyk( TracingDetailsFragmentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), tracingStatus = tracingStatus, backgroundModeStatus = backgroundModeStatus, riskLevelStorage = riskLevelStorage, diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/viewmodels/MockViewModelModule.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/viewmodels/MockViewModelModule.kt index 1a4527b7915608bcdfc0614792b1b70f972edd53..e8467d83b0b64e0ea0167875af8fdd40607b47b1 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/viewmodels/MockViewModelModule.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/viewmodels/MockViewModelModule.kt @@ -1,35 +1,16 @@ package testhelpers.viewmodels -import android.os.Bundle -import androidx.lifecycle.SavedStateHandle -import androidx.savedstate.SavedStateRegistryOwner import dagger.Module import dagger.Provides import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider @Module class MockViewModelModule { @Provides - fun viewmodelFactoryProvider(): CWAViewModelFactoryProvider.Factory { - val factory = object : CWAViewModelFactoryProvider.Factory { - override fun create( - savedStateOwner: SavedStateRegistryOwner, - defaultSavedState: Bundle?, - assistAction: ((CWAViewModelFactory<out CWAViewModel>, SavedStateHandle) -> CWAViewModel)? - ): CWAViewModelFactoryProvider { - return CWAViewModelFactoryProvider( - CREATORS, - savedStateOwner, - defaultSavedState, - assistAction - ) - } - } - return factory - } + fun viewModelCreators(): MutableMap<Class<out CWAViewModel>, CWAViewModelFactory<out CWAViewModel>> = + CREATORS companion object { val CREATORS: MutableMap<Class<out CWAViewModel>, CWAViewModelFactory<out CWAViewModel>> = diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/miscinfo/MiscInfoFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/miscinfo/MiscInfoFragmentViewModel.kt index 13ea95fa031a605e9a58ce1db8e68a994388d683..e18ff6c3b707444e820f5099f3e7894ab1b7ca45 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/miscinfo/MiscInfoFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/miscinfo/MiscInfoFragmentViewModel.kt @@ -4,7 +4,8 @@ import android.content.Context import androidx.core.content.pm.PackageInfoCompat import androidx.lifecycle.asLiveData import com.google.android.gms.common.GoogleApiAvailability -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.nearby.ENFClient import de.rki.coronawarnapp.risk.TimeVariables import de.rki.coronawarnapp.storage.tracing.TracingIntervalRepository @@ -59,6 +60,6 @@ class MiscInfoFragmentViewModel @AssistedInject constructor( emit(TimeVariables.getActiveTracingDaysInRetentionPeriod()) }.asLiveData(context = dispatcherProvider.Default) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<MiscInfoFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/appconfig/ui/AppConfigTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/appconfig/ui/AppConfigTestFragmentViewModel.kt index 5bfa83d2572ce9f0ae0b4f247a554f940802b661..1f98cae3fcf67d5e8a92ca3d9287f6276d2709d5 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/appconfig/ui/AppConfigTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/appconfig/ui/AppConfigTestFragmentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.test.appconfig.ui import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.storage.TestSettings import de.rki.coronawarnapp.util.coroutine.DispatcherProvider @@ -43,6 +44,6 @@ class AppConfigTestFragmentViewModel @AssistedInject constructor( testSettings.isDeviceTimeCheckDisabled.update { !it } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<AppConfigTestFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt index 44df7a14121621e5a45b754fda316295fabcfcff..5afc9df32ae1e4290997cee48a551bc2efb35073 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.test.contactdiary.ui import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation @@ -107,6 +108,6 @@ class ContactDiaryTestFragmentViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<ContactDiaryTestFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt index 44d3e0cd08a2688511a8c20589bfe3b428659d36..e9a0f09eaa938220072ebd67ede73acd960e1262 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt @@ -4,7 +4,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.map -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.bugreporting.event.BugEvent import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.bugreporting.storage.repository.BugRepository @@ -54,6 +55,6 @@ class SettingsCrashReportViewModel @AssistedInject constructor( " ${bugEvent.stackTrace}\n\n" + " # Corresponding Log: \n\n ${bugEvent.logHistory}" - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SettingsCrashReportViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt index e9b4bda7f7e71be71c837acb467adb246991fadb..9d959a1db3d704e446d3261244ba1afe18c5a99d 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.test.debugoptions.ui -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.environment.EnvironmentSetup.Type.Companion.toEnvironmentType import de.rki.coronawarnapp.test.debugoptions.ui.EnvironmentState.Companion.toEnvironmentState @@ -28,6 +29,6 @@ class DebugOptionsFragmentViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<DebugOptionsFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/keydownload/ui/KeyDownloadTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/keydownload/ui/KeyDownloadTestFragmentViewModel.kt index c2dbd49fc3f779fa2ca313fcb178db5a8c10c42e..8a51aa231b6baa023ff5e86c79207811bc0ea8bd 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/keydownload/ui/KeyDownloadTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/keydownload/ui/KeyDownloadTestFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.test.keydownload.ui import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.diagnosiskeys.download.KeyPackageSyncTool import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.storage.TestSettings @@ -75,6 +76,6 @@ class KeyDownloadTestFragmentViewModel @AssistedInject constructor( keyCacheRepository.delete(listOf(it.info)) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<KeyDownloadTestFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt index e77ffd41f8af9eb568535b6b5db26d3d53798345..a28ebf709415d0b45cb8659663f31e5a26823eb0 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.test.menu.ui import androidx.lifecycle.MutableLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.miscinfo.MiscInfoFragment import de.rki.coronawarnapp.test.appconfig.ui.AppConfigTestFragment import de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryTestFragment @@ -36,6 +37,6 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() { showTestScreenEvent.postValue(it) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<TestMenuFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt index 986a7e4afc7250bc93e2b75f6caf2f52e80649c4..7066eebed08a92c3d5ea7bf7c7a8a6edd853b983 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt @@ -5,8 +5,9 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings @@ -238,7 +239,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( testSettings.fakeExposureWindows.update { newMode } } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<TestRiskLevelCalculationFragmentCWAViewModel> { fun create( handle: SavedStateHandle, diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/submission/ui/SubmissionTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/submission/ui/SubmissionTestFragmentViewModel.kt index 9e7e02ee68a667d4e349c8363ebc56da907dd9d0..72cc9dc6f4a52eca9231e7754d20b7070b46666b 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/submission/ui/SubmissionTestFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/submission/ui/SubmissionTestFragmentViewModel.kt @@ -6,7 +6,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey import com.google.gson.Gson -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryStorage @@ -118,6 +119,6 @@ class SubmissionTestFragmentViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestFragmentViewModel> } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/tasks/ui/TestTaskControllerFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/tasks/ui/TestTaskControllerFragmentViewModel.kt index 82b274e9b6196175afa396580e06d77b5d3114e2..f3b909b0fa3fc8cd8f1d026a48a29f6cd3ee59c1 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/tasks/ui/TestTaskControllerFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/tasks/ui/TestTaskControllerFragmentViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.test.tasks.ui import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.liveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.TaskFactory @@ -116,6 +117,6 @@ class TestTaskControllerFragmentViewModel @AssistedInject constructor( ) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<TestTaskControllerFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/assets/technical.html b/Corona-Warn-App/src/main/assets/technical.html index dda06e116150dda51bb8e558e213c853403ae150..ab77f6194cdfd05a32003406119f4b122e70df65 100644 --- a/Corona-Warn-App/src/main/assets/technical.html +++ b/Corona-Warn-App/src/main/assets/technical.html @@ -66,10 +66,6 @@ Licensor: Google<br/> Website: https://github.com/google/dagger<br/> License: Apache 2.0 -<p>Component: AssistedInject<br/> - Licensor: Square<br/> - Website: https://github.com/square/AssistedInject<br/> - License: Apache 2.0 <p>Component: Conscrypt<br/> Licensor: Conscrypt<br/> Website: https://github.com/google/conscrypt<br/> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/ApplicationConfigurationInvalidException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/ApplicationConfigurationInvalidException.kt index 15ec3692bca6ba188403a32a2e17de7a8dac0fea..f1c0cfe2ac1bec5eb1c4325e1a158ae00b80ced0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/ApplicationConfigurationInvalidException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/internal/ApplicationConfigurationInvalidException.kt @@ -1,12 +1,12 @@ package de.rki.coronawarnapp.appconfig.internal import de.rki.coronawarnapp.exception.reporting.ErrorCodes -import de.rki.coronawarnapp.exception.reporting.ReportedException +import de.rki.coronawarnapp.util.security.InvalidSignatureException class ApplicationConfigurationInvalidException( cause: Exception? = null, - message: String? = null -) : ReportedException( + message: String +) : InvalidSignatureException( code = ErrorCodes.APPLICATION_CONFIGURATION_INVALID.code, message = message, cause = cause diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogViewModel.kt index 027a0793c78a9ddf22a4b8fce44172235f6daa65..d31e9f34ad2ea548ae5195d118ad0a5dba0d0bd5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.bugreporting.debuglog.ui import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R import de.rki.coronawarnapp.bugreporting.debuglog.DebugLogger import de.rki.coronawarnapp.nearby.ENFClient @@ -110,6 +111,6 @@ class DebugLogViewModel @AssistedInject constructor( val currentSize: Long = 0 ) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<DebugLogViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTask.kt index ea44af92ffad79c7a14259ee2859fefcb3f41947..84a790910d2e197e902526256e1cd94f3ecdf5e6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTask.kt @@ -29,6 +29,9 @@ class ContactDiaryCleanTask @Inject constructor( retentionCalculation.clearObsoleteContactDiaryPersonEncounters() Timber.tag(TAG).d("Obsolete contact diary person encounters cleaned up") + retentionCalculation.clearObsoleteRiskPerDate() + Timber.tag(TAG).d("Obsolete Aggregated Risk Per Date Results cleaned up") + object : Task.Result {} } catch (error: Exception) { Timber.tag(TAG).e(error) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt index fb90610e64d2411828caa572ce39d89888118750..658a0c4861fd1098befb51d8548f851bef32f3f2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt @@ -4,6 +4,7 @@ import dagger.Reusable import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.repo.DefaultContactDiaryRepository +import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.util.TimeStamper import kotlinx.coroutines.flow.first import org.joda.time.Days @@ -14,27 +15,31 @@ import javax.inject.Inject @Reusable class ContactDiaryRetentionCalculation @Inject constructor( private val timeStamper: TimeStamper, - private val repository: DefaultContactDiaryRepository + private val repository: DefaultContactDiaryRepository, + private val riskLevelStorage: RiskLevelStorage ) { + fun isOutOfRetention(date: LocalDate): Boolean = RETENTION_DAYS < getDaysDiff(date).also { + Timber.d("Days diff: $it") + } + fun getDaysDiff(dateSaved: LocalDate): Int { val today = LocalDate(timeStamper.nowUTC) return Days.daysBetween(dateSaved, today).days } fun filterContactDiaryLocationVisits(list: List<ContactDiaryLocationVisit>): List<ContactDiaryLocationVisit> { - return list.filter { entity -> RETENTION_DAYS < getDaysDiff(entity.date) } + return list.filter { entity -> isOutOfRetention(entity.date) } } fun filterContactDiaryPersonEncounters(list: List<ContactDiaryPersonEncounter>): List<ContactDiaryPersonEncounter> { - return list.filter { entity -> RETENTION_DAYS < getDaysDiff(entity.date) } + return list.filter { entity -> isOutOfRetention(entity.date) } } suspend fun clearObsoleteContactDiaryLocationVisits() { val list = repository.locationVisits.first() Timber.d("Contact Diary Location Visits total count: ${list.size}") - val toDeleteList = - list.filter { entity -> RETENTION_DAYS < getDaysDiff(entity.date).also { Timber.d("Days diff: $it") } } + val toDeleteList = list.filter { entity -> isOutOfRetention(entity.date) } Timber.d("Contact Diary Location Visits to be deleted: ${toDeleteList.size}") repository.deleteLocationVisits(toDeleteList) } @@ -42,15 +47,22 @@ class ContactDiaryRetentionCalculation @Inject constructor( suspend fun clearObsoleteContactDiaryPersonEncounters() { val list = repository.personEncounters.first() Timber.d("Contact Diary Persons Encounters total count: ${list.size}") - val toDeleteList = - list.filter { entity -> RETENTION_DAYS < getDaysDiff(entity.date).also { Timber.d("Days diff: $it") } } + val toDeleteList = list.filter { entity -> isOutOfRetention(entity.date) } Timber.d("Contact Diary Persons Encounters to be deleted: ${toDeleteList.size}") repository.deletePersonEncounters(toDeleteList) } + suspend fun clearObsoleteRiskPerDate() { + val list = riskLevelStorage.aggregatedRiskPerDateResults.first() + Timber.d("Aggregated Risk Per Date Results total count: ${list.size}") + val toDeleteList = list.filter { risk -> isOutOfRetention(risk.day) } + Timber.d("AggregatedRiskPerDateResult to be deleted count: ${toDeleteList.size}") + riskLevelStorage.deleteAggregatedRiskPerDateResults(toDeleteList) + } + companion object { /** - * Contact diary data retention in days 14+2 + * Contact diary data retention in days 15+1 */ const val RETENTION_DAYS = 16 } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionWorker.kt index 129c4b2c87dd0d8ef5d404ff9beeb4976c4d4bc9..ee821d957b56ad81d3899a433adaa385ca86d791 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.contactdiary.retention import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.task.submitBlocking @@ -41,7 +42,7 @@ class ContactDiaryRetentionWorker @AssistedInject constructor( return Result.success() } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<ContactDiaryRetentionWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryPreferences.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryPreferences.kt new file mode 100644 index 0000000000000000000000000000000000000000..a54333b6846b5da78a0e26a85883788cd0843c76 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryPreferences.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.contactdiary.storage + +import android.content.Context +import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.preferences.clearAndNotify +import de.rki.coronawarnapp.util.preferences.createFlowPreference +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ContactDiaryPreferences @Inject constructor( + @AppContext val context: Context +) { + + private val prefs by lazy { + context.getSharedPreferences("contact_diary_localdata", Context.MODE_PRIVATE) + } + + val onboardingStatusOrder = prefs.createFlowPreference( + key = "contact_diary_onboardingstatus", + defaultValue = ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED.ordinal + ) + + fun clear() { + prefs.clearAndNotify() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryActivity.kt index d0529b41e9afaeebcb6b169b6869596481a2595e..3aeaba9b7b028c060f73cb40144619832d7057d2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryActivity.kt @@ -37,7 +37,7 @@ class ContactDiaryActivity : AppCompatActivity(), HasAndroidInjector { val navInflater = navController.navInflater val graph = navInflater.inflate(R.navigation.contact_diary_nav_graph) - if (settings.isOnboarded.value) { + if (settings.onboardingStatus == ContactDiarySettings.OnboardingStatus.RISK_STATUS_1_12) { graph.startDestination = R.id.contactDiaryOverviewFragment } else { graph.startDestination = R.id.contactDiaryOnboardingFragment diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettings.kt index a7b81a1d343baa3996baaea86c11825fd64d44af..060f31727d356c5b69c4f561ecc37b3894f62713 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettings.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettings.kt @@ -1,27 +1,21 @@ package de.rki.coronawarnapp.contactdiary.ui -import android.content.Context -import de.rki.coronawarnapp.util.di.AppContext -import de.rki.coronawarnapp.util.preferences.clearAndNotify -import de.rki.coronawarnapp.util.preferences.createFlowPreference +import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import javax.inject.Inject import javax.inject.Singleton @Singleton -class ContactDiarySettings @Inject constructor( - @AppContext val context: Context -) { +class ContactDiarySettings @Inject constructor(val preferences: ContactDiaryPreferences) { - private val prefs by lazy { - context.getSharedPreferences("contact_diary_localdata", Context.MODE_PRIVATE) - } - - val isOnboarded = prefs.createFlowPreference( - key = "contact_diary_onboarded", - defaultValue = false - ) + var onboardingStatus: OnboardingStatus + get() { + val order = preferences.onboardingStatusOrder.value + return OnboardingStatus.values().find { it.order == order } ?: OnboardingStatus.NOT_ONBOARDED + } + set(value) = preferences.onboardingStatusOrder.update { value.order } - fun clear() { - prefs.clearAndNotify() + enum class OnboardingStatus(val order: Int) { + NOT_ONBOARDED(0), + RISK_STATUS_1_12(1) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt index 8f7cd9cc526fe839e26f3ecf37bb20e58fb897c0..d023f0b5de9dfe2cf71ab969f898afb257abec96 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt @@ -2,8 +2,9 @@ package de.rki.coronawarnapp.contactdiary.ui.day import android.content.Context import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.ui.day.tabs.ContactDiaryDayTab import de.rki.coronawarnapp.contactdiary.util.getLocale @@ -50,7 +51,7 @@ class ContactDiaryDayViewModel @AssistedInject constructor( val dayTextContentDescription: (Context) -> String ) - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<ContactDiaryDayViewModel> { fun create(selectedDay: String): ContactDiaryDayViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt index f015f8fec6ff0bb6e17a0b721da710ef3ec2146a..f232f9f94455927fc274822a9e9711c084e7c091 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt @@ -1,8 +1,9 @@ package de.rki.coronawarnapp.contactdiary.ui.day.tabs.location import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit @@ -73,7 +74,7 @@ class ContactDiaryLocationListViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<ContactDiaryLocationListViewModel> { fun create(selectedDay: String): ContactDiaryLocationListViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt index 6c6db515aa028b0350d00097e0e761b8396a5102..3051b597a387d14d5b2391e2a4ba307f14c05ff9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt @@ -1,8 +1,9 @@ package de.rki.coronawarnapp.contactdiary.ui.day.tabs.person import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPersonEncounter @@ -72,7 +73,7 @@ class ContactDiaryPersonListViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<ContactDiaryPersonListViewModel> { fun create(selectedDay: String): ContactDiaryPersonListViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt index 10fee646bb08563435b849a06d1095d50491bd10..76f288d4efe549ed745434ba65f962250eb5ccba 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.contactdiary.ui.edit import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryLocationEntity @@ -49,7 +50,7 @@ class ContactDiaryEditLocationsViewModel @AssistedInject constructor( navigationEvent.postValue(NavigationEvent.ShowLocationDetailSheet(location.toContactDiaryLocationEntity())) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<ContactDiaryEditLocationsViewModel> sealed class NavigationEvent { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt index 5faebc22c02b5f81740782609bb8d727030afeec..052ec29c6a45352a75b540a203b2175f0a942d34 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.contactdiary.ui.edit import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryPersonEntity @@ -50,7 +51,7 @@ class ContactDiaryEditPersonsViewModel @AssistedInject constructor( navigationEvent.postValue(NavigationEvent.ShowPersonDetailSheet(person.toContactDiaryPersonEntity())) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<ContactDiaryEditPersonsViewModel> sealed class NavigationEvent { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt index a0278cf0be4357544ec41100f71490aed5685499..2d7674dffacdd0f4a5f0da5476ab913e2f278f13 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt @@ -66,8 +66,8 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin } } - fun onboardingComplete() { - settings.isOnboarded.update { true } + private fun onboardingComplete() { + settings.onboardingStatus = ContactDiarySettings.OnboardingStatus.RISK_STATUS_1_12 } override fun onResume() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragmentViewModel.kt index 0edf3bfbb12316fba9c48c122412eca5c78afca9..cce9ca8057393898c8c94e4932d5591b2a0c0750 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragmentViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.contactdiary.ui.onboarding -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -20,6 +21,6 @@ class ContactDiaryOnboardingFragmentViewModel @AssistedInject constructor() : CW routeToScreen.postValue(ContactDiaryOnboardingNavigationEvents.NavigateToPrivacyFragment) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<ContactDiaryOnboardingFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt index 544f637eb2a476a629a997edcbc49dc583e0c54f..98db07ac3bdde6c0d7f40fcdfcabe0e4a867ba9a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt @@ -1,14 +1,20 @@ package de.rki.coronawarnapp.contactdiary.ui.overview import android.content.Context +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryCleanTask import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ListItem +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult +import de.rki.coronawarnapp.risk.storage.RiskLevelStorage +import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.ui.SingleLiveEvent @@ -25,7 +31,8 @@ import java.util.Locale class ContactDiaryOverviewViewModel @AssistedInject constructor( taskController: TaskController, dispatcherProvider: DispatcherProvider, - contactDiaryRepository: ContactDiaryRepository + contactDiaryRepository: ContactDiaryRepository, + riskLevelStorage: RiskLevelStorage ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { val routeToScreen: SingleLiveEvent<ContactDiaryOverviewNavigationEvents> = SingleLiveEvent() @@ -36,13 +43,16 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( private val locationVisitsFlow = contactDiaryRepository.locationVisits private val personEncountersFlow = contactDiaryRepository.personEncounters + private val riskLevelPerDateFlow = riskLevelStorage.aggregatedRiskPerDateResults + val listItems = combine( flowOf(dates), locationVisitsFlow, - personEncountersFlow - ) { dateList, locationVisitList, personEncounterList -> - createListItemList(dateList, locationVisitList, personEncounterList) - }.asLiveData() + personEncountersFlow, + riskLevelPerDateFlow + ) { dateList, locationVisitList, personEncounterList, riskLevelPerDateList -> + createListItemList(dateList, locationVisitList, personEncounterList, riskLevelPerDateList) + }.asLiveData(dispatcherProvider.Default) init { taskController.submit( @@ -56,12 +66,14 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( private fun createListItemList( dateList: List<LocalDate>, locationVisitList: List<ContactDiaryLocationVisit>, - personEncounterList: List<ContactDiaryPersonEncounter> + personEncounterList: List<ContactDiaryPersonEncounter>, + riskLevelPerDateList: List<AggregatedRiskPerDateResult> ): List<ListItem> { Timber.v( "createListItemList(dateList=$dateList, " + "locationVisitList=$locationVisitList, " + - "personEncounterList=$personEncounterList)" + "personEncounterList=$personEncounterList)" + + "riskLevelPerDateList=$riskLevelPerDateList" ) return dateList .map { @@ -69,10 +81,33 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( .apply { data.addPersonEncountersForDate(personEncounterList, date) data.addLocationVisitsForDate(locationVisitList, date) + risk = riskLevelPerDateList + .firstOrNull { riskLevelPerDate -> riskLevelPerDate.day == it } + ?.toRisk(data.isEmpty()) } } } + private fun AggregatedRiskPerDateResult.toRisk(noLocationOrPerson: Boolean): ListItem.Risk { + @StringRes val title: Int + @DrawableRes val drawableId: Int + + @StringRes val body: Int = when (noLocationOrPerson) { + true -> R.string.contact_diary_risk_body + false -> R.string.contact_diary_risk_body_extended + } + + if (this.riskLevel == RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH) { + title = R.string.contact_diary_high_risk_title + drawableId = R.drawable.ic_high_risk_alert + } else { + title = R.string.contact_diary_low_risk_title + drawableId = R.drawable.ic_low_risk_alert + } + + return ListItem.Risk(title, body, drawableId) + } + private fun MutableList<ListItem.Data>.addPersonEncountersForDate( personEncounterList: List<ContactDiaryPersonEncounter>, date: LocalDate @@ -149,10 +184,11 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( // According to tech spec german locale only private fun LocalDate.toFormattedString(): String = toString("dd.MM.yyyy", Locale.GERMAN) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<ContactDiaryOverviewViewModel> companion object { - const val DAY_COUNT = 14 + // Today + 14 days + const val DAY_COUNT = 15 } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewAdapter.kt index f802f1833b09a3c8fa863feca7ebc5e737d13a04..3d57e88257e01addceea0de27aade9966ef6290c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewAdapter.kt @@ -33,18 +33,33 @@ class ContactDiaryOverviewAdapter( BaseAdapter.VH(R.layout.contact_diary_overview_list_item, parent), BindableVH<ListItem, ContactDiaryOverviewListItemBinding> { + private val nestedItemAdapter by lazy { ContactDiaryOverviewNestedAdapter() } + override val viewBinding: Lazy<ContactDiaryOverviewListItemBinding> = lazy { ContactDiaryOverviewListItemBinding.bind(itemView) } override val onBindData: ContactDiaryOverviewListItemBinding.(item: ListItem, payloads: List<Any>) -> Unit = { item, _ -> - val nestedItemAdapter = ContactDiaryOverviewNestedAdapter(item, onItemSelectionListener) - viewBinding.value.contactDiaryOverviewNestedRecyclerView.adapter = nestedItemAdapter - contactDiaryOverviewElementName.text = dateFormatter(item.date) - contactDiaryOverviewElementName.contentDescription = dateFormatterForAccessibility(item.date) + contactDiaryOverviewNestedRecyclerView.adapter = nestedItemAdapter + contactDiaryOverviewNestedRecyclerView.suppressLayout(true) contactDiaryOverviewElementBody.setOnClickListener { onItemSelectionListener(item) } + + contactDiaryOverviewElementName.apply { + text = dateFormatter(item.date) + contentDescription = dateFormatterForAccessibility(item.date) + } + contactDiaryOverviewNestedElementGroup.isGone = item.data.isEmpty() nestedItemAdapter.setItems(item.data) + + contactDiaryOverviewNestedListItemRisk.apply { + item.risk?.let { + this.contactDiaryOverviewRiskItem.isGone = false + this.contactDiaryOverviewItemRiskTitle.text = context.getString(it.title) + this.contactDiaryOverviewItemRiskBody.text = context.getString(it.body) + this.contactDiaryOverviewRiskItemImage.setImageResource(it.drawableId) + } ?: run { this.contactDiaryOverviewRiskItem.isGone = true } + } } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt index ef8ed0d379f71c73fc990ef0d8404425467ec89d..3cd03fb893d398f01c93349236532b0b3742452f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt @@ -7,10 +7,7 @@ import de.rki.coronawarnapp.databinding.ContactDiaryOverviewNestedListItemBindin import de.rki.coronawarnapp.ui.lists.BaseAdapter import de.rki.coronawarnapp.util.lists.BindableVH -class ContactDiaryOverviewNestedAdapter( - private val element: ListItem, - private val onItemSelectionListener: (ListItem) -> Unit -) : BaseAdapter<ContactDiaryOverviewNestedAdapter.NestedItemViewHolder>() { +class ContactDiaryOverviewNestedAdapter : BaseAdapter<ContactDiaryOverviewNestedAdapter.NestedItemViewHolder>() { private val dataList: MutableList<ListItem.Data> = mutableListOf() @@ -42,10 +39,7 @@ class ContactDiaryOverviewNestedAdapter( contactDiaryOverviewElementName.contentDescription = when (key.type) { ListItem.Type.LOCATION -> context.getString(R.string.accessibility_location, key.text) ListItem.Type.PERSON -> context.getString(R.string.accessibility_person, key.text) - else -> key.text } - - contactDiaryOverviewElementNestedContainer.setOnClickListener { onItemSelectionListener(element) } } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt index 52f9784ba768ce77c6c6a674401de5b36c3e3f70..f45c8704107408158b164ae2f94edb6ba52f9176 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt @@ -1,18 +1,27 @@ package de.rki.coronawarnapp.contactdiary.ui.overview.adapter +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import org.joda.time.LocalDate data class ListItem( val date: LocalDate ) { val data: MutableList<Data> = mutableListOf() + var risk: Risk? = null data class Data( - val drawableId: Int, + @DrawableRes val drawableId: Int, val text: String, val type: Type ) + data class Risk( + @StringRes val title: Int, + @StringRes val body: Int, + @DrawableRes val drawableId: Int + ) + enum class Type { LOCATION, PERSON } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt index df1d4d017933dfdc0c76c4c8adae18847e2b1dfe..14263b7dc44788935daf863f5432db8251f22fef 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt @@ -1,8 +1,9 @@ package de.rki.coronawarnapp.contactdiary.ui.sheets.location import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity @@ -92,7 +93,7 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor private val TAG = ContactDiaryLocationBottomSheetDialogViewModel::class.java.simpleName } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<ContactDiaryLocationBottomSheetDialogViewModel> { fun create(addedAt: String?): ContactDiaryLocationBottomSheetDialogViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt index 8eae6adf7fbe1cea611928bcbfd1e02dd53cc7af..6f96510f35871ea2425d763025523792d04e9087 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt @@ -1,8 +1,9 @@ package de.rki.coronawarnapp.contactdiary.ui.sheets.person import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPerson import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity @@ -92,7 +93,7 @@ class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor( private val TAG = ContactDiaryPersonBottomSheetDialogViewModel::class.java.simpleName } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<ContactDiaryPersonBottomSheetDialogViewModel> { fun create(addedAt: String?): ContactDiaryPersonBottomSheetDialogViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationOneTimeWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationOneTimeWorker.kt index 9eec045cc1776734cbad047b9c822b3db65cbe65..cf57b18025fb25c47cc33e2b440ae3a2251fa19a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationOneTimeWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationOneTimeWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.deadman import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import de.rki.coronawarnapp.worker.BackgroundConstants import timber.log.Timber @@ -39,7 +40,7 @@ class DeadmanNotificationOneTimeWorker @AssistedInject constructor( return result } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<DeadmanNotificationOneTimeWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationPeriodicWorker.kt index efd968e15ea379950f0a7b1dacc8e6a39d6ee66f..7a0c1619192e9a059a5af9cc5b51399e11bfb132 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/deadman/DeadmanNotificationPeriodicWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.deadman import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import de.rki.coronawarnapp.worker.BackgroundConstants import timber.log.Timber @@ -41,7 +42,7 @@ class DeadmanNotificationPeriodicWorker @AssistedInject constructor( return result } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<DeadmanNotificationPeriodicWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt index 3e934b008469f3fee8a64f8a4e8d5308126f7ff1..0a79ad34c664fba087c2b1c1bbc134aaa3f66fb3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt @@ -1,10 +1,12 @@ package de.rki.coronawarnapp.exception +import de.rki.coronawarnapp.R import de.rki.coronawarnapp.exception.reporting.ErrorCodes import de.rki.coronawarnapp.exception.reporting.ReportedIOException class CwaWebSecurityException(cause: Throwable) : ReportedIOException( ErrorCodes.CWA_WEB_SECURITY_PROBLEM.code, "An error occurred while trying to establish a secure connection to the server", - cause + cause, + R.string.errors_ssl_certificate_deactivated ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ExposureStateUpdateWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ExposureStateUpdateWorker.kt index 5332793bffb9cff398fb6bae06816085bd8a7086..818ddd3b1706d090b5a516e65b6adad742a3195d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ExposureStateUpdateWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ExposureStateUpdateWorker.kt @@ -4,8 +4,9 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.google.android.gms.common.api.ApiException -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.risk.RiskLevelTask @@ -33,7 +34,7 @@ class ExposureStateUpdateWorker @AssistedInject constructor( return Result.success() } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<ExposureStateUpdateWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/TracingPermissionHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/TracingPermissionHelper.kt index a7c968e5cdb089a8d133e814c3f146bcf986db76..2e5107cb6d4655cfac6c6108d6ea48b569f9d793 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/TracingPermissionHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/TracingPermissionHelper.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.nearby import android.app.Activity import android.content.Intent import androidx.annotation.VisibleForTesting -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.util.coroutine.AppScope import kotlinx.coroutines.CoroutineScope @@ -90,7 +91,7 @@ class TracingPermissionHelper @AssistedInject constructor( internal const val TRACING_PERMISSION_REQUESTCODE = 3010 } - @AssistedInject.Factory + @AssistedFactory interface Factory { fun create(callback: Callback): TracingPermissionHelper } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt index 3d8dc7205d3755f631f6bdc1aae85a4835c84f20..bfab7ff3ed278eac3087256f8f0fafd93ecf9d82 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt @@ -232,7 +232,8 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels { mostRecentDateWithLowRisk = mostRecentDateWithLowRisk, mostRecentDateWithHighRisk = mostRecentDateWithHighRisk, numberOfDaysWithLowRisk = numberOfDaysWithLowRisk, - numberOfDaysWithHighRisk = numberOfDaysWithHighRisk + numberOfDaysWithHighRisk = numberOfDaysWithHighRisk, + aggregatedRiskPerDateResults = exposureHistory ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt index 99c140888f23c365690f3671430e015cf966d96b..51d830d0babc8a05adff6603bcb83a7c65546211 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt @@ -1,10 +1,20 @@ package de.rki.coronawarnapp.risk.result import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass +import org.joda.time.Instant +import org.joda.time.LocalDate +import org.joda.time.format.ISODateTimeFormat data class AggregatedRiskPerDateResult( val dateMillisSinceEpoch: Long, val riskLevel: RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel, val minimumDistinctEncountersWithLowRisk: Int, val minimumDistinctEncountersWithHighRisk: Int -) +) { + val day: LocalDate + get() { + val dateFormatter = ISODateTimeFormat.date() + val dateString = Instant.ofEpochMilli(dateMillisSinceEpoch).toString(dateFormatter) + return LocalDate.parse(dateString, dateFormatter) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt index 1a6d0c6ccd2d06f0925464843cb924b45acfe241..45130825e126c6de46031398ab03f32f59205029 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt @@ -11,7 +11,8 @@ data class AggregatedRiskResult( val mostRecentDateWithLowRisk: Instant?, val mostRecentDateWithHighRisk: Instant?, val numberOfDaysWithLowRisk: Int, - val numberOfDaysWithHighRisk: Int + val numberOfDaysWithHighRisk: Int, + var aggregatedRiskPerDateResults: List<AggregatedRiskPerDateResult>? = null ) { fun isIncreasedRisk(): Boolean = totalRiskLevel == ProtoRiskLevel.HIGH diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt index 2a36edaf10098261ca75af0618026fd18e0dc072..5e0afe1c1c9cdcd75892fcff552aeeeafa9621e9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt @@ -2,8 +2,10 @@ package de.rki.coronawarnapp.risk.storage import de.rki.coronawarnapp.risk.RiskLevelResult import de.rki.coronawarnapp.risk.RiskLevelTaskResult +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao +import de.rki.coronawarnapp.risk.storage.internal.riskresults.toPersistedAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.riskresults.toPersistedRiskResult import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDaoWrapper import de.rki.coronawarnapp.risk.storage.legacy.RiskLevelResultMigrator @@ -24,6 +26,7 @@ abstract class BaseRiskLevelStorage constructor( private val database by lazy { riskResultDatabaseFactory.create() } internal val riskResultsTables by lazy { database.riskResults() } internal val exposureWindowsTables by lazy { database.exposureWindows() } + internal val aggregatedRiskPerDateResultTables by lazy { database.aggregatedRiskPerDate() } abstract val storedResultLimit: Int @@ -104,6 +107,10 @@ abstract class BaseRiskLevelStorage constructor( Timber.d("Storing RiskLevelResult took %dms.", (System.currentTimeMillis() - startTime)) } + result.aggregatedRiskResult?.aggregatedRiskPerDateResults?.let { + insertAggregatedRiskPerDateResults(it) + } + resultToPersist.id } catch (e: Exception) { Timber.e(e, "Failed to store latest result: %s", result) @@ -128,6 +135,37 @@ abstract class BaseRiskLevelStorage constructor( deletedOrphanedExposureWindows() } + override val aggregatedRiskPerDateResults: Flow<List<AggregatedRiskPerDateResult>> by lazy { + aggregatedRiskPerDateResultTables.allEntries() + .map { it.map { + persistedAggregatedRiskPerDateResult -> + persistedAggregatedRiskPerDateResult.toAggregatedRiskPerDateResult() + } } + .shareLatest(tag = TAG, scope = scope) + } + + private suspend fun insertAggregatedRiskPerDateResults( + aggregatedRiskPerDateResults: List<AggregatedRiskPerDateResult> + ) { + Timber.d("insertAggregatedRiskPerDateResults(aggregatedRiskPerDateResults=%s)", aggregatedRiskPerDateResults) + try { + aggregatedRiskPerDateResultTables.insertRisk(aggregatedRiskPerDateResults.map { + it.toPersistedAggregatedRiskPerDateResult() + }) + } catch (e: Exception) { + Timber.e(e, "Failed to store risk level per date results") + } + } + + override suspend fun deleteAggregatedRiskPerDateResults(results: List<AggregatedRiskPerDateResult>) { + Timber.d("deleteAggregatedRiskPerDateResults(results=%s)", results) + try { + aggregatedRiskPerDateResultTables.delete(results.map { it.toPersistedAggregatedRiskPerDateResult() }) + } catch (e: Exception) { + Timber.e(e, "Failed to delete risk level per date results") + } + } + internal abstract suspend fun storeExposureWindows(storedResultId: String, result: RiskLevelResult) internal abstract suspend fun deletedOrphanedExposureWindows() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt index ad3ecd9a7c66dcfa8205d6500bc7d6c2b200214f..5a7ce88567b188b066fc4c01448d6fa5474f8fad 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.risk.storage import de.rki.coronawarnapp.risk.RiskLevelResult +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult import kotlinx.coroutines.flow.Flow interface RiskLevelStorage { @@ -28,6 +29,15 @@ interface RiskLevelStorage { */ val latestAndLastSuccessful: Flow<List<RiskLevelResult>> + /** + * Risk level per date/day + * Used by contact diary overview + * Item with newest date first. + */ + val aggregatedRiskPerDateResults: Flow<List<AggregatedRiskPerDateResult>> + + suspend fun deleteAggregatedRiskPerDateResults(results: List<AggregatedRiskPerDateResult>) + suspend fun storeResult(result: RiskLevelResult) suspend fun clear() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/RiskResultDatabase.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/RiskResultDatabase.kt index 83c786c58bf2531cfe654e160ad6588ddbd12f31..c3d77875667f2ff46b30bfd9723aac339aae99c6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/RiskResultDatabase.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/RiskResultDatabase.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.risk.storage.internal import android.content.Context import androidx.room.Dao import androidx.room.Database +import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query @@ -10,6 +11,8 @@ import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration1To2 +import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration2To3 +import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDao import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDaoWrapper @@ -24,9 +27,10 @@ import javax.inject.Inject entities = [ PersistedRiskLevelResultDao::class, PersistedExposureWindowDao::class, - PersistedExposureWindowDao.PersistedScanInstance::class + PersistedExposureWindowDao.PersistedScanInstance::class, + PersistedAggregatedRiskPerDateResult::class ], - version = 2, + version = 3, exportSchema = true ) @TypeConverters( @@ -40,6 +44,8 @@ abstract class RiskResultDatabase : RoomDatabase() { abstract fun exposureWindows(): ExposureWindowsDao + abstract fun aggregatedRiskPerDate(): AggregatedRiskPerDateResultDao + @Dao interface RiskResultsDao { @Query("SELECT * FROM riskresults ORDER BY monotonicId DESC") @@ -80,13 +86,25 @@ abstract class RiskResultDatabase : RoomDatabase() { suspend fun deleteByRiskResultId(riskResultIds: List<String>): Int } + @Dao + interface AggregatedRiskPerDateResultDao { + @Query("SELECT * FROM riskperdate ORDER BY dateMillisSinceEpoch DESC") + fun allEntries(): Flow<List<PersistedAggregatedRiskPerDateResult>> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertRisk(persistedAggregatedRiskPerDateResults: List<PersistedAggregatedRiskPerDateResult>) + + @Delete + suspend fun delete(persistedAggregatedRiskPerDateResults: List<PersistedAggregatedRiskPerDateResult>) + } + class Factory @Inject constructor(@AppContext private val context: Context) { fun create(databaseName: String = DATABASE_NAME): RiskResultDatabase { Timber.d("Instantiating risk result database.") return Room .databaseBuilder(context, RiskResultDatabase::class.java, databaseName) - .addMigrations(RiskResultDatabaseMigration1To2) + .addMigrations(RiskResultDatabaseMigration1To2, RiskResultDatabaseMigration2To3) .build() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/migrations/RiskResultDatabaseMigration2To3.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/migrations/RiskResultDatabaseMigration2To3.kt new file mode 100644 index 0000000000000000000000000000000000000000..b9a4749d0674488bbbfffa0ec6486dad4c53aaba --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/migrations/RiskResultDatabaseMigration2To3.kt @@ -0,0 +1,23 @@ +package de.rki.coronawarnapp.risk.storage.internal.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.reporting.report +import timber.log.Timber + +@Suppress("MaxLineLength") +object RiskResultDatabaseMigration2To3 : Migration(2, 3) { + override fun migrate(database: SupportSQLiteDatabase) { + try { + Timber.i("Attempting migration 2->3...") + database.execSQL( + "CREATE TABLE IF NOT EXISTS `riskperdate` (`dateMillisSinceEpoch` INTEGER NOT NULL, `riskLevel` INTEGER NOT NULL, `minimumDistinctEncountersWithLowRisk` INTEGER NOT NULL, `minimumDistinctEncountersWithHighRisk` INTEGER NOT NULL, PRIMARY KEY(`dateMillisSinceEpoch`))" + ) + Timber.i("Migration 2->3 successful.") + } catch (e: Exception) { + e.report(ExceptionCategory.INTERNAL, "Migration 2->3 failed. Could not create new table riskperdate") + throw e + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt new file mode 100644 index 0000000000000000000000000000000000000000..c3693602599d42d9f97607853ed7221c25172487 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt @@ -0,0 +1,32 @@ +package de.rki.coronawarnapp.risk.storage.internal.riskresults + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult +import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass + +@Suppress("MaxLineLength") +@Entity(tableName = "riskperdate") +data class PersistedAggregatedRiskPerDateResult( + @PrimaryKey @ColumnInfo(name = "dateMillisSinceEpoch") val dateMillisSinceEpoch: Long, + @ColumnInfo(name = "riskLevel") val riskLevel: RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel, + @ColumnInfo(name = "minimumDistinctEncountersWithLowRisk") val minimumDistinctEncountersWithLowRisk: Int, + @ColumnInfo(name = "minimumDistinctEncountersWithHighRisk") val minimumDistinctEncountersWithHighRisk: Int +) { + fun toAggregatedRiskPerDateResult(): AggregatedRiskPerDateResult = + AggregatedRiskPerDateResult( + dateMillisSinceEpoch = dateMillisSinceEpoch, + riskLevel = riskLevel, + minimumDistinctEncountersWithLowRisk = minimumDistinctEncountersWithLowRisk, + minimumDistinctEncountersWithHighRisk = minimumDistinctEncountersWithHighRisk + ) +} + +fun AggregatedRiskPerDateResult.toPersistedAggregatedRiskPerDateResult(): PersistedAggregatedRiskPerDateResult = + PersistedAggregatedRiskPerDateResult( + dateMillisSinceEpoch = dateMillisSinceEpoch, + riskLevel = riskLevel, + minimumDistinctEncountersWithLowRisk = minimumDistinctEncountersWithLowRisk, + minimumDistinctEncountersWithHighRisk = minimumDistinctEncountersWithHighRisk + ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatisticsModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatisticsModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..24db16888aa78f3f78d4d6811b908827509178e1 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatisticsModule.kt @@ -0,0 +1,70 @@ +package de.rki.coronawarnapp.statistics + +import android.content.Context +import dagger.Module +import dagger.Provides +import de.rki.coronawarnapp.environment.download.DownloadCDNHttpClient +import de.rki.coronawarnapp.environment.download.DownloadCDNServerUrl +import de.rki.coronawarnapp.statistics.source.StatisticsApiV1 +import de.rki.coronawarnapp.util.di.AppContext +import okhttp3.Cache +import okhttp3.OkHttpClient +import org.joda.time.Duration +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.io.File +import java.util.concurrent.TimeUnit +import javax.inject.Qualifier +import javax.inject.Singleton + +@Module +class StatisticsModule { + + @Singleton + @Provides + @Statistics + fun cacheDir( + @AppContext context: Context + ): File = File(context.cacheDir, "statistics") + + @Singleton + @Provides + @Statistics + fun httpCache( + @Statistics cacheDir: File + ): Cache = Cache(File(cacheDir, "cache_http"), DEFAULT_CACHE_SIZE) + + @Singleton + @Provides + fun api( + @DownloadCDNHttpClient client: OkHttpClient, + @DownloadCDNServerUrl url: String, + gsonConverterFactory: GsonConverterFactory, + @Statistics cache: Cache + ): StatisticsApiV1 { + val configHttpClient = client.newBuilder().apply { + cache(cache) + connectTimeout(HTTP_TIMEOUT.millis, TimeUnit.MILLISECONDS) + readTimeout(HTTP_TIMEOUT.millis, TimeUnit.MILLISECONDS) + writeTimeout(HTTP_TIMEOUT.millis, TimeUnit.MILLISECONDS) + callTimeout(HTTP_TIMEOUT.millis, TimeUnit.MILLISECONDS) + }.build() + + return Retrofit.Builder() + .client(configHttpClient) + .baseUrl(url) + .addConverterFactory(gsonConverterFactory) + .build() + .create(StatisticsApiV1::class.java) + } + + companion object { + private const val DEFAULT_CACHE_SIZE = 5 * 1024 * 1024L // 5MB + private val HTTP_TIMEOUT = Duration.standardSeconds(10) + } +} + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class Statistics diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatsItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatsItem.kt index bc55a2f769e0aa2b867f0df33482e13f54dc51be..5a8cd74e476f77263d71a8a3fd743b84ccfd43fa 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatsItem.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/StatsItem.kt @@ -1,30 +1,117 @@ package de.rki.coronawarnapp.statistics -import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure import org.joda.time.Instant +import timber.log.Timber data class StatisticsData( - val items: List<StatsItem> + val items: List<StatsItem> = emptyList() ) { val isDataAvailable: Boolean = items.isNotEmpty() + + override fun toString(): String { + return "StatisticsData(cards=${items.map { it.cardType.name + " " + it.updatedAt }})" + } } -sealed class StatsItem(val cardId: Int) { +sealed class StatsItem(val cardType: Type) { abstract val updatedAt: Instant - abstract val keyFigures: List<KeyFigureCardOuterClass.KeyFigure> + abstract val keyFigures: List<KeyFigure> + + enum class Type(val id: Int) { + INFECTION(1), + INCIDENCE(2), + KEYSUBMISSION(3), + SEVEN_DAY_RVALUE(4) + } + + abstract fun requireValidity() } data class InfectionStats( override val updatedAt: Instant, - override val keyFigures: List<KeyFigureCardOuterClass.KeyFigure> -) : StatsItem(cardId = 1) + override val keyFigures: List<KeyFigure> +) : StatsItem(cardType = Type.INFECTION) { + + val newInfections: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } + + val sevenDayAverage: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.SECONDARY } + + val total: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.TERTIARY } + + override fun requireValidity() { + require(keyFigures.size == 3) + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.PRIMARY }) { + Timber.w("InfectionStats is missing primary value") + } + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.SECONDARY }) { + Timber.w("InfectionStats is missing secondary value") + } + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.TERTIARY }) { + Timber.w("InfectionStats is missing secondary value") + } + } +} data class IncidenceStats( override val updatedAt: Instant, - override val keyFigures: List<KeyFigureCardOuterClass.KeyFigure> -) : StatsItem(cardId = 2) + override val keyFigures: List<KeyFigure> +) : StatsItem(cardType = Type.INCIDENCE) { + + val sevenDayIncidence: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } + + override fun requireValidity() { + require(keyFigures.size == 1) + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.PRIMARY }) { + Timber.w("IncidenceStats is missing primary value") + } + } +} data class KeySubmissionsStats( override val updatedAt: Instant, - override val keyFigures: List<KeyFigureCardOuterClass.KeyFigure> -) : StatsItem(cardId = 3) + override val keyFigures: List<KeyFigure> +) : StatsItem(cardType = Type.KEYSUBMISSION) { + + val keySubmissions: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } + + val sevenDayAverage: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.SECONDARY } + + val total: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.TERTIARY } + + override fun requireValidity() { + require(keyFigures.size == 3) + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.PRIMARY }) { + Timber.w("KeySubmissionsStats is missing primary value") + } + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.SECONDARY }) { + Timber.w("KeySubmissionsStats is missing secondary value") + } + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.TERTIARY }) { + Timber.w("KeySubmissionsStats is missing secondary value") + } + } +} + +data class SevenDayRValue( + override val updatedAt: Instant, + override val keyFigures: List<KeyFigure> +) : StatsItem(cardType = Type.SEVEN_DAY_RVALUE) { + + val reproductionNumber: KeyFigure + get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } + + override fun requireValidity() { + require(keyFigures.size == 1) + requireNotNull(keyFigures.singleOrNull { it.rank == KeyFigure.Rank.PRIMARY }) { + Timber.w("SevenDayRValue is missing primary value") + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/InvalidStatisticsSignatureException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/InvalidStatisticsSignatureException.kt new file mode 100644 index 0000000000000000000000000000000000000000..3cb39d826e6a7e41c8dd9ac52fcc9aebdff9ea93 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/InvalidStatisticsSignatureException.kt @@ -0,0 +1,9 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.exception.reporting.ErrorCodes +import de.rki.coronawarnapp.util.security.InvalidSignatureException + +class InvalidStatisticsSignatureException(message: String) : InvalidSignatureException( + code = ErrorCodes.APPLICATION_CONFIGURATION_INVALID.code, + message = message +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsApiV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsApiV1.kt new file mode 100644 index 0000000000000000000000000000000000000000..7bdc561573500c4205a4c8700225e1df212eeff8 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsApiV1.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.statistics.source + +import okhttp3.ResponseBody +import retrofit2.Response +import retrofit2.http.GET + +interface StatisticsApiV1 { + + @GET("/version/v1/stats") + suspend fun getStatistics(): Response<ResponseBody> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsCache.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsCache.kt new file mode 100644 index 0000000000000000000000000000000000000000..01e4aca577c45f2c507576182436b3c00dbe04ef --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsCache.kt @@ -0,0 +1,40 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.statistics.Statistics +import timber.log.Timber +import java.io.File +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StatisticsCache @Inject constructor( + @Statistics cacheDir: File +) { + + private val cacheFile = File(cacheDir, "cache_raw") + + fun load(): ByteArray? = try { + if (cacheFile.exists()) cacheFile.readBytes() else null + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to load raw statistics from cache.") + null + } + + fun save(data: ByteArray?) { + if (data == null) { + if (cacheFile.exists() && cacheFile.delete()) { + Timber.tag(TAG).d("Cache file was deleted.") + } + return + } + if (cacheFile.exists()) { + Timber.tag(TAG).d("Overwriting with new data (size=%d)", data.size) + } + cacheFile.parentFile?.mkdirs() + cacheFile.writeBytes(data) + } + + companion object { + const val TAG = "StatisticsCache" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsParser.kt new file mode 100644 index 0000000000000000000000000000000000000000..9121af6fa7f7b3ab6ed2e5b353f656aaf33bee95 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsParser.kt @@ -0,0 +1,61 @@ +package de.rki.coronawarnapp.statistics.source + +import dagger.Reusable +import de.rki.coronawarnapp.server.protocols.internal.stats.StatisticsOuterClass +import de.rki.coronawarnapp.statistics.IncidenceStats +import de.rki.coronawarnapp.statistics.InfectionStats +import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.SevenDayRValue +import de.rki.coronawarnapp.statistics.StatisticsData +import de.rki.coronawarnapp.statistics.StatsItem +import org.joda.time.Instant +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class StatisticsParser @Inject constructor() { + + fun parse(rawData: ByteArray): StatisticsData { + val parsed = StatisticsOuterClass.Statistics.parseFrom(rawData) + + if (parsed.cardIdSequenceCount != parsed.keyFigureCardsCount) { + Timber.tag(TAG).w( + "Cards have been hidden (sequenceCount=%d != cardCount=%d)", + parsed.cardIdSequenceCount, parsed.keyFigureCardsCount + ) + } + + val mappedItems: Set<StatsItem> = parsed.keyFigureCardsList.mapNotNull { rawCard -> + try { + val updatedAt = Instant.ofEpochSecond(rawCard.header.updatedAt) + val keyFigures = rawCard.keyFiguresList + when (StatsItem.Type.values().singleOrNull { it.id == rawCard.header.cardId }) { + StatsItem.Type.INFECTION -> InfectionStats(updatedAt = updatedAt, keyFigures = keyFigures) + StatsItem.Type.INCIDENCE -> IncidenceStats(updatedAt = updatedAt, keyFigures = keyFigures) + StatsItem.Type.KEYSUBMISSION -> KeySubmissionsStats(updatedAt = updatedAt, keyFigures = keyFigures) + StatsItem.Type.SEVEN_DAY_RVALUE -> SevenDayRValue(updatedAt = updatedAt, keyFigures = keyFigures) + null -> null.also { Timber.tag(TAG).e("Unknown statistics type: %s", rawCard) } + }.also { + Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) + it?.requireValidity() + } + } catch (e: Exception) { + Timber.tag(TAG).e("Failed to parse raw card: %s", rawCard) + null + } + }.toSet() + + val orderedItems = parsed.cardIdSequenceList.mapNotNull { cardId -> + mappedItems.singleOrNull { it.cardType.id == cardId }.also { + if (it == null) Timber.tag(TAG).w("There was no card data for ID=%d", cardId) + } + } + return StatisticsData(items = orderedItems).also { + Timber.tag(TAG).d("Parsed statistics data, %d cards.", it.items.size) + } + } + + companion object { + const val TAG = "StatisticsParser" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt index 2e657fa0ec0308d18ec7f4744cfa6c458832175e..3d254d25e69e67155345cda31c084c9238453d27 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt @@ -1,102 +1,100 @@ package de.rki.coronawarnapp.statistics.source -import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass -import de.rki.coronawarnapp.statistics.IncidenceStats -import de.rki.coronawarnapp.statistics.InfectionStats -import de.rki.coronawarnapp.statistics.KeySubmissionsStats import de.rki.coronawarnapp.statistics.StatisticsData -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope +import de.rki.coronawarnapp.util.coroutine.AppScope +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.device.ForegroundState +import de.rki.coronawarnapp.util.flow.HotDataFlow +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.launch -import org.joda.time.Instant +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.joda.time.Duration +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton -class StatisticsProvider @Inject constructor() { +class StatisticsProvider @Inject constructor( + @AppScope private val scope: CoroutineScope, + private val server: StatisticsServer, + private val localCache: StatisticsCache, + private val parser: StatisticsParser, + foregroundState: ForegroundState, + dispatcherProvider: DispatcherProvider +) { - private val currentInternal = MutableStateFlow(StatisticsData(items = emptyList())) - val current: Flow<StatisticsData> = currentInternal.filterNotNull() + private val statisticsData = HotDataFlow( + loggingTag = TAG, + scope = scope, + coroutineContext = dispatcherProvider.IO, + sharingBehavior = SharingStarted.WhileSubscribed( + stopTimeoutMillis = Duration.standardSeconds(5).millis, + replayExpirationMillis = 0 + ) + ) { + try { + fromCache() ?: fromServer() + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to get data from server.") + StatisticsData() + } + } + + val current: Flow<StatisticsData> = statisticsData.data init { - // Mock data - GlobalScope.launch(context = Dispatchers.IO) { - val statisticsData = StatisticsData( - items = listOf( - InfectionStats( - updatedAt = Instant.ofEpochMilli(1604839761), - keyFigures = listOf( - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY - value = 14714.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND - trendSemantic = - KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC - }.build(), - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY - value = 11981.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING - trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE - }.build(), KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY - value = 429181.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND - trendSemantic = - KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC - }.build() - ) - ), - IncidenceStats( - updatedAt = Instant.ofEpochMilli(1604839761), - keyFigures = listOf( - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY - value = 98.9 - decimals = 1 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND - trendSemantic = - KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC - }.build() - ) - ), - KeySubmissionsStats( - updatedAt = Instant.ofEpochMilli(1604839761), - keyFigures = listOf( - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY - value = 1514.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND - trendSemantic = - KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC - }.build(), - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY - value = 1812.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.DECREASING - trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE - }.build(), - KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { - rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY - value = 20922.0 - decimals = 0 - trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND - trendSemantic = - KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC - }.build() - ) - ) - ) - ) - currentInternal.emit(statisticsData) + foregroundState.isInForeground + .onEach { + if (it) { + Timber.tag(TAG).d("App moved to foreground triggering statistics update.") + triggerUpdate() + } + } + .catch { Timber.tag(TAG).e("Failed to trigger statistics update.") } + .launchIn(scope) + } + + private fun fromCache(): StatisticsData? = try { + Timber.tag(TAG).d("fromCache()") + localCache.load()?.let { parser.parse(it) }?.also { + Timber.tag(TAG).d("Parsed from cache: %s", it) + } + } catch (e: Exception) { + Timber.tag(TAG).w(e, "Failed to parse cached data.") + null + } + + private suspend fun fromServer(): StatisticsData { + Timber.tag(TAG).d("fromServer()") + val rawData = server.getRawStatistics() + return parser.parse(rawData).also { + Timber.tag(TAG).d("Parsed from server: %s", it) + localCache.save(rawData) } } + + fun triggerUpdate() { + Timber.tag(TAG).d("triggerUpdate()") + statisticsData.updateSafely { + try { + fromServer() + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to update statistics.") + this@updateSafely // return previous data + } + } + } + + fun clear() { + Timber.d("clear()") + server.clear() + localCache.save(null) + } + + companion object { + const val TAG = "StatisticsProvider" + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsServer.kt new file mode 100644 index 0000000000000000000000000000000000000000..11526a01c55cf71e0cbeca327521b8dc53535e3c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsServer.kt @@ -0,0 +1,58 @@ +package de.rki.coronawarnapp.statistics.source + +import dagger.Lazy +import dagger.Reusable +import de.rki.coronawarnapp.statistics.Statistics +import de.rki.coronawarnapp.util.ZipHelper.readIntoMap +import de.rki.coronawarnapp.util.ZipHelper.unzip +import de.rki.coronawarnapp.util.security.VerificationKeys +import okhttp3.Cache +import retrofit2.HttpException +import timber.log.Timber +import java.io.IOException +import javax.inject.Inject + +@Reusable +class StatisticsServer @Inject constructor( + private val api: Lazy<StatisticsApiV1>, + private val verificationKeys: VerificationKeys, + @Statistics val cache: Cache +) { + + suspend fun getRawStatistics(): ByteArray { + Timber.tag(TAG).d("Fetching statistics.") + + val response = api.get().getStatistics() + if (!response.isSuccessful) throw HttpException(response) + + return with( + requireNotNull(response.body()) { "Response was successful but body was null" } + ) { + val fileMap = byteStream().unzip().readIntoMap() + + val exportBinary = fileMap[EXPORT_BINARY_FILE_NAME] + val exportSignature = fileMap[EXPORT_SIGNATURE_FILE_NAME] + + if (exportBinary == null || exportSignature == null) { + throw IOException("Unknown files: ${fileMap.keys}") + } + + if (verificationKeys.hasInvalidSignature(exportBinary, exportSignature)) { + throw InvalidStatisticsSignatureException(message = "Statistics signature did not match.") + } + + exportBinary + } + } + + fun clear() { + Timber.d("clear()") + cache.evictAll() + } + + companion object { + private const val EXPORT_BINARY_FILE_NAME = "export.bin" + private const val EXPORT_SIGNATURE_FILE_NAME = "export.sig" + private const val TAG = "StatisticsServer" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/StatisticsExplanationFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/StatisticsExplanationFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..669d806cde31ffe6cd300de7b57e9a334ca8e538 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/StatisticsExplanationFragment.kt @@ -0,0 +1,43 @@ +package de.rki.coronawarnapp.statistics.ui + +import android.os.Bundle +import android.view.View +import android.view.accessibility.AccessibilityEvent +import androidx.fragment.app.Fragment +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.FragmentStatisticsExplanationBinding +import de.rki.coronawarnapp.util.setUrl +import de.rki.coronawarnapp.util.ui.viewBindingLazy + +/** + * The fragment displays static informative content to the user + * and represents one way to gain more detailed understanding of the + * statistics and its trends. + * + */ + +class StatisticsExplanationFragment : Fragment(R.layout.fragment_statistics_explanation) { + + private val binding: FragmentStatisticsExplanationBinding by viewBindingLazy() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setButtonOnClickListener() + binding.statisticsExplanationSevenDayRValueText.setUrl( + R.string.statistics_explanation_seven_day_r_value_text, + R.string.statistics_explanation_seven_day_r_link_label, + R.string.statistics_explanation_faq_url + ) + } + + override fun onResume() { + super.onResume() + binding.statisticsExplanationContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) + } + + private fun setButtonOnClickListener() { + binding.statisticsExplanationHeaderButtonBack.buttonIcon.setOnClickListener { + activity?.onBackPressed() + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/TrendArrowView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/TrendArrowView.kt new file mode 100644 index 0000000000000000000000000000000000000000..0d9dff5404bede8698e7caae28e372473b57086b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/TrendArrowView.kt @@ -0,0 +1,58 @@ +package de.rki.coronawarnapp.statistics.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.core.content.ContextCompat +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure.Trend.DECREASING +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure.TrendSemantic.POSITIVE + +class TrendArrowView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + private val imageView: ImageView + + init { + LayoutInflater.from(context) + .inflate(R.layout.statistics_trend_view, this, true) + imageView = findViewById(R.id.trend) + } + + fun setTrend( + trend: KeyFigureCardOuterClass.KeyFigure.Trend, + trendSemantic: KeyFigureCardOuterClass.KeyFigure.TrendSemantic + ) { + with(imageView) { + rotation = when (trend) { + INCREASING -> -45F + DECREASING -> 45F + else -> 0F + } + + background = ContextCompat.getDrawable( + context, when (trendSemantic) { + POSITIVE -> R.drawable.bg_statistics_trend_positive + NEGATIVE -> R.drawable.bg_statistics_trend_negative + else -> R.drawable.bg_statistics_trend_neutral + } + ) + + contentDescription = context.getString( + when (trend) { + INCREASING -> R.string.statistics_trend_increasing + DECREASING -> R.string.statistics_trend_decreasing + else -> R.string.statistics_trend_stable + } + ) + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardAdapter.kt index 815fd900b219ca80676a6594561ef6ead342bee5..1805ced630701cbb84f251a1c0ede9c0cdb40f0a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardAdapter.kt @@ -6,10 +6,12 @@ import androidx.viewbinding.ViewBinding import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.SevenDayRValue import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter.ItemVH import de.rki.coronawarnapp.statistics.ui.homecards.cards.IncidenceCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.InfectionsCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.KeySubmissionsCard +import de.rki.coronawarnapp.statistics.ui.homecards.cards.SevenDayRValueCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.StatisticsCardItem import de.rki.coronawarnapp.util.lists.BindableVH import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter @@ -30,7 +32,8 @@ class StatisticsCardAdapter : ModularAdapter<ItemVH<StatisticsCardItem, ViewBind DataBinderMod<StatisticsCardItem, ItemVH<StatisticsCardItem, ViewBinding>>(data), TypedVHCreatorMod({ data[it].stats is InfectionStats }) { InfectionsCard(it) }, TypedVHCreatorMod({ data[it].stats is IncidenceStats }) { IncidenceCard(it) }, - TypedVHCreatorMod({ data[it].stats is KeySubmissionsStats }) { KeySubmissionsCard(it) } + TypedVHCreatorMod({ data[it].stats is KeySubmissionsStats }) { KeySubmissionsCard(it) }, + TypedVHCreatorMod({ data[it].stats is SevenDayRValue }) { SevenDayRValueCard(it) } ).let { modules.addAll(it) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardPaddingDecorator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardPaddingDecorator.kt index 541289aea5e66c33bb01cb62f1da16fc840d368b..272ae7379212521e1aeb58edd74cd0bfcaff36d1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardPaddingDecorator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsCardPaddingDecorator.kt @@ -8,6 +8,7 @@ import androidx.recyclerview.widget.RecyclerView.ItemDecoration class StatisticsCardPaddingDecorator( @DimenRes val startPadding: Int, + @DimenRes val verticalPadding: Int, @DimenRes val endPadding: Int = startPadding, @DimenRes val cardDistance: Int = startPadding ) : ItemDecoration() { @@ -22,18 +23,24 @@ class StatisticsCardPaddingDecorator( val resources = parent.context.resources val adapter = parent.adapter + val distance = resources.getDimensionPixelSize(cardDistance) when (itemPosition) { 0 -> { outRect.left = resources.getDimensionPixelSize(startPadding) + if (adapter?.itemCount == 1) { + outRect.right = resources.getDimensionPixelSize(endPadding) + } else { + outRect.right = distance + } } (adapter?.itemCount ?: Int.MAX_VALUE) - 1 -> { outRect.right = resources.getDimensionPixelSize(endPadding) } else -> { - val distance = resources.getDimensionPixelSize(cardDistance) - outRect.left = distance / 2 - outRect.right = distance / 2 + outRect.right = distance } } + outRect.bottom = resources.getDimensionPixelSize(verticalPadding) + outRect.top = resources.getDimensionPixelSize(verticalPadding) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCard.kt index a0c5bfc70a5b6885c945705d7c30b13ac6dd5732..59939f7f25a9052f33f2a838575c206db18c9763 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCard.kt @@ -4,6 +4,7 @@ import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsScrollcontainerBinding import de.rki.coronawarnapp.statistics.StatisticsData @@ -23,11 +24,19 @@ class StatisticsHomeCard( override val viewBinding = lazy { HomeStatisticsScrollcontainerBinding.bind(itemView).apply { statisticsRecyclerview.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + setHasFixedSize(false) adapter = statsAdapter + layoutManager = StatisticsLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) itemAnimator = DefaultItemAnimator() - addItemDecoration(StatisticsCardPaddingDecorator(startPadding = R.dimen.spacing_small)) + addItemDecoration( + StatisticsCardPaddingDecorator( + startPadding = R.dimen.spacing_small, + cardDistance = R.dimen.spacing_tiny, + verticalPadding = R.dimen.spacing_tiny + ) + ) } + PagerSnapHelper().attachToRecyclerView(statisticsRecyclerview) } } @@ -45,5 +54,24 @@ class StatisticsHomeCard( val onHelpAction: (StatsItem) -> Unit ) : HomeItem { override val stableId: Long = Item::class.java.name.hashCode().toLong() + + // ignore onHelpAction so that view is not re-drawn when only the onHelpAction click listener is updated + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Item + + if (data != other.data) return false + if (stableId != other.stableId) return false + + return true + } + + override fun hashCode(): Int { + var result = data.hashCode() + result = 31 * result + stableId.hashCode() + return result + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsLayoutManager.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsLayoutManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..08bca00ebf1fd5f6d85ca2c10ba85f48146f45af --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsLayoutManager.kt @@ -0,0 +1,47 @@ +package de.rki.coronawarnapp.statistics.ui.homecards + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +/** + * A custom RecyclerView.LayoutManager implementation that extends + * the height of items to match RecyclerView. +**/ +class StatisticsLayoutManager : LinearLayoutManager { + constructor(context: Context?) : super(context) + + constructor( + context: Context?, + @RecyclerView.Orientation orientation: Int, + reverseLayout: Boolean + ) : super(context, orientation, reverseLayout) + + constructor( + context: Context?, + attrs: AttributeSet?, + defStyleAttr: Int, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { + return spanLayoutSize(super.generateDefaultLayoutParams()) + } + + override fun generateLayoutParams(c: Context, attrs: AttributeSet): RecyclerView.LayoutParams { + return spanLayoutSize(super.generateLayoutParams(c, attrs)) + } + + override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams { + return spanLayoutSize(super.generateLayoutParams(lp)) + } + + private fun spanLayoutSize(layoutParams: RecyclerView.LayoutParams): RecyclerView.LayoutParams { + return RecyclerView.LayoutParams( + layoutParams.width, + RecyclerView.LayoutParams.MATCH_PARENT + ) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/IncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/IncidenceCard.kt index 9f59e9e629cc2a18a5127242f3c8745c28441091..d5433c1fc991a1a57f63f20ce4918cffc4558367 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/IncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/IncidenceCard.kt @@ -5,6 +5,9 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsIncidenceLayoutBinding import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter +import de.rki.coronawarnapp.statistics.util.formatStatisticalValue +import de.rki.coronawarnapp.statistics.util.getLocalizedSpannableString +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class IncidenceCard(parent: ViewGroup) : StatisticsCardAdapter.ItemVH<StatisticsCardItem, HomeStatisticsCardsIncidenceLayoutBinding>( @@ -22,7 +25,19 @@ class IncidenceCard(parent: ViewGroup) : override val onBindData: HomeStatisticsCardsIncidenceLayoutBinding.( item: StatisticsCardItem, payloads: List<Any> - ) -> Unit = { item, payloads -> - item.stats as IncidenceStats + ) -> Unit = { item, _ -> + + infoStatistics.setOnClickListener { + item.onHelpAction.invoke(item.stats) + } + + with(item.stats as IncidenceStats) { + primaryLabel.text = getPrimaryLabel(context) + primaryValue.text = getLocalizedSpannableString( + context, + formatStatisticalValue(context, sevenDayIncidence.value, sevenDayIncidence.decimals) + ) + trendArrow.setTrend(sevenDayIncidence.trend, sevenDayIncidence.trendSemantic) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/InfectionsCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/InfectionsCard.kt index ad912ed593bb0bfd746d5b4c2866e0751b1c24fc..c1fad337fe9ace15e485796f28f913e75eff677d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/InfectionsCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/InfectionsCard.kt @@ -5,6 +5,8 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsInfectionsLayoutBinding import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter +import de.rki.coronawarnapp.statistics.util.formatStatisticalValue +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class InfectionsCard(parent: ViewGroup) : StatisticsCardAdapter.ItemVH<StatisticsCardItem, HomeStatisticsCardsInfectionsLayoutBinding>( @@ -22,7 +24,18 @@ class InfectionsCard(parent: ViewGroup) : override val onBindData: HomeStatisticsCardsInfectionsLayoutBinding.( item: StatisticsCardItem, payloads: List<Any> - ) -> Unit = { item, payloads -> - item.stats as InfectionStats + ) -> Unit = { item, _ -> + + infoStatistics.setOnClickListener { + item.onHelpAction.invoke(item.stats) + } + + with(item.stats as InfectionStats) { + primaryLabel.text = getPrimaryLabel(context) + primaryValue.text = formatStatisticalValue(context, newInfections.value, newInfections.decimals) + secondaryValue.text = formatStatisticalValue(context, sevenDayAverage.value, sevenDayAverage.decimals) + tertiaryValue.text = formatStatisticalValue(context, total.value, total.decimals) + trendArrow.setTrend(sevenDayAverage.trend, sevenDayAverage.trendSemantic) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/KeySubmissionsCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/KeySubmissionsCard.kt index 292c285b62e85997cdb169a868f28455e38b75fa..08218528f43b04ab12ec4bff2ecf1812d41e5238 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/KeySubmissionsCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/KeySubmissionsCard.kt @@ -5,6 +5,8 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsKeysubmissionsLayoutBinding import de.rki.coronawarnapp.statistics.KeySubmissionsStats import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter +import de.rki.coronawarnapp.statistics.util.formatStatisticalValue +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class KeySubmissionsCard(parent: ViewGroup) : StatisticsCardAdapter.ItemVH<StatisticsCardItem, HomeStatisticsCardsKeysubmissionsLayoutBinding>( @@ -22,7 +24,18 @@ class KeySubmissionsCard(parent: ViewGroup) : override val onBindData: HomeStatisticsCardsKeysubmissionsLayoutBinding.( item: StatisticsCardItem, payloads: List<Any> - ) -> Unit = { item, payloads -> - item.stats as KeySubmissionsStats + ) -> Unit = { item, _ -> + + infoStatistics.setOnClickListener { + item.onHelpAction.invoke(item.stats) + } + + with(item.stats as KeySubmissionsStats) { + primaryLabel.text = getPrimaryLabel(context) + primaryValue.text = formatStatisticalValue(context, keySubmissions.value, keySubmissions.decimals) + secondaryValue.text = formatStatisticalValue(context, sevenDayAverage.value, sevenDayAverage.decimals) + tertiaryValue.text = formatStatisticalValue(context, total.value, total.decimals) + trendArrow.setTrend(sevenDayAverage.trend, sevenDayAverage.trendSemantic) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/SevenDayRValueCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/SevenDayRValueCard.kt new file mode 100644 index 0000000000000000000000000000000000000000..b21614ef32e06d843ac78f8181399a7863a82270 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/SevenDayRValueCard.kt @@ -0,0 +1,43 @@ +package de.rki.coronawarnapp.statistics.ui.homecards.cards + +import android.view.ViewGroup +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.HomeStatisticsCardsSevendayrvalueLayoutBinding +import de.rki.coronawarnapp.statistics.SevenDayRValue +import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter +import de.rki.coronawarnapp.statistics.util.formatStatisticalValue +import de.rki.coronawarnapp.statistics.util.getLocalizedSpannableString +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel + +class SevenDayRValueCard(parent: ViewGroup) : + StatisticsCardAdapter.ItemVH<StatisticsCardItem, HomeStatisticsCardsSevendayrvalueLayoutBinding>( + R.layout.home_statistics_cards_basecard_layout, parent + ) { + + override val viewBinding = lazy { + HomeStatisticsCardsSevendayrvalueLayoutBinding.inflate( + layoutInflater, + itemView.findViewById(R.id.card_container), + true + ) + } + + override val onBindData: HomeStatisticsCardsSevendayrvalueLayoutBinding.( + item: StatisticsCardItem, + payloads: List<Any> + ) -> Unit = { item, _ -> + + infoStatistics.setOnClickListener { + item.onHelpAction.invoke(item.stats) + } + + with(item.stats as SevenDayRValue) { + primaryLabel.text = getPrimaryLabel(context) + primaryValue.text = getLocalizedSpannableString( + context, + formatStatisticalValue(context, reproductionNumber.value, reproductionNumber.decimals) + ) + trendArrow.setTrend(reproductionNumber.trend, reproductionNumber.trendSemantic) + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/StatisticsCardItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/StatisticsCardItem.kt index 0130712ea939546e20dda1aebbcd28b896594f62..d2aeb5ab12c5c7567e3b6f826faad18ac5c0c6ec 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/StatisticsCardItem.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/StatisticsCardItem.kt @@ -8,5 +8,5 @@ data class StatisticsCardItem( val onHelpAction: (StatsItem) -> Unit ) : HasStableId { - override val stableId: Long = stats.cardId.toLong() + override val stableId: Long = stats.cardType.id.toLong() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/AccessibilityHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/AccessibilityHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..1c9bff6793ff87ac42d657cb863d75944a74a172 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/AccessibilityHelper.kt @@ -0,0 +1,13 @@ +package de.rki.coronawarnapp.statistics.util + +import android.content.Context +import android.text.SpannableString +import android.text.style.LocaleSpan +import de.rki.coronawarnapp.contactdiary.util.getLocale + +/** + * returns localized spannable string so that screen readers read out decimal values appropriately + */ +fun getLocalizedSpannableString(context: Context, source: String) = SpannableString(source).apply { + setSpan(LocaleSpan(context.getLocale()), 0, this.length, 0) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatter.kt new file mode 100644 index 0000000000000000000000000000000000000000..de0b9c04d9e580c5909ce3b5f8932d592ccbede5 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatter.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.statistics.util + +import android.content.Context +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.contactdiary.util.getLocale +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols + +fun formatStatisticalValue( + context: Context, + value: Double, + decimals: Int +): String { + + val locale = context.getLocale() + + // return strings like "12.7 Mio" for large values + if (value >= 10_000_000) { + return DecimalFormat("#,###.0", DecimalFormatSymbols(locale)) + .format(value / 1_000_000) + " ${context.getString(R.string.statistics_value_suffix_million)}" + } + + return when (decimals) { + in Int.MIN_VALUE..0 -> DecimalFormat("#,###", DecimalFormatSymbols(locale)) + 1 -> DecimalFormat("#,###.#", DecimalFormatSymbols(locale)) + else -> DecimalFormat("#,###.##", DecimalFormatSymbols(locale)) + }.format(value) +} 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 24e670aff5571534d7c60025bda5e30b05fb36b8..d5ba704606a07240986aa470d4d280ee963ad24a 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 @@ -22,6 +22,7 @@ object LocalData { private const val PREFERENCE_HAS_RISK_STATUS_LOWERED = "preference_has_risk_status_lowered" + /**************************************************** * ONBOARDING DATA ****************************************************/ @@ -342,40 +343,19 @@ object LocalData { putInt(PKEY_POSITIVE_TEST_RESULT_REMINDER_COUNT, value) } - /** - * Gets the decision if background jobs are enabled - * - * @return - */ - fun isBackgroundJobEnabled(): Boolean = getSharedPreferenceInstance().getBoolean( - CoronaWarnApplication.getAppContext().getString(R.string.preference_background_job_allowed), - false - ) - - /** - * Gets the boolean if the user has mobile data enabled - * - * @return - */ - fun isMobileDataEnabled(): Boolean = getSharedPreferenceInstance().getBoolean( - CoronaWarnApplication.getAppContext().getString(R.string.preference_mobile_data_allowed), - false - ) - /**************************************************** * SUBMISSION DATA ****************************************************/ + private const val PREFERENCE_REGISTRATION_TOKEN = "preference_registration_token" + /** * Gets the registration token that is needed for the submission process * * @return the registration token */ - fun registrationToken(): String? = getSharedPreferenceInstance().getString( - CoronaWarnApplication.getAppContext() - .getString(R.string.preference_registration_token), - null - ) + fun registrationToken(): String? = getSharedPreferenceInstance() + .getString(PREFERENCE_REGISTRATION_TOKEN, null) /** * Sets the registration token that is needed for the submission process @@ -384,11 +364,7 @@ object LocalData { */ fun registrationToken(value: String?) { getSharedPreferenceInstance().edit(true) { - putString( - CoronaWarnApplication.getAppContext() - .getString(R.string.preference_registration_token), - value - ) + putString(PREFERENCE_REGISTRATION_TOKEN, value) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/auto/SubmissionWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/auto/SubmissionWorker.kt index 40746db268446f6b307c2b0bace487e30b580a74..f3e61a8a07a72862ff723777ef0b746b5fefb4dd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/auto/SubmissionWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/auto/SubmissionWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.submission.auto import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.submission.task.SubmissionTask @@ -42,7 +43,7 @@ class SubmissionWorker @AssistedInject constructor( Result.retry() } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<SubmissionWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/data/tekhistory/TEKHistoryUpdater.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/data/tekhistory/TEKHistoryUpdater.kt index 0ffae418729480ee2df0354f881aa4701d993985..bc536c4cfb91c44cb99f3d7a107f06a8a49dd5be 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/data/tekhistory/TEKHistoryUpdater.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/data/tekhistory/TEKHistoryUpdater.kt @@ -4,8 +4,9 @@ import android.app.Activity import android.content.Intent import androidx.annotation.VisibleForTesting import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.ENFClient @@ -142,7 +143,7 @@ class TEKHistoryUpdater @AssistedInject constructor( fun onError(error: Throwable) } - @AssistedInject.Factory + @AssistedFactory interface Factory { fun create(callback: Callback): TEKHistoryUpdater } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt index ad7ef54540293384b782067a0303f87e0f2077af..a21a5486700a3f9249a3480c4bf2095ecc15e2b0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.tracing.states -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.nearby.modules.detectiontracker.latestSubmission import de.rki.coronawarnapp.risk.RiskState @@ -97,7 +98,7 @@ class TracingStateProvider @AssistedInject constructor( .onEach { Timber.d("TracingStateProvider FLOW emission: %s", it) } .onCompletion { Timber.v("TracingStateProvider FLOW completed.") } - @AssistedInject.Factory + @AssistedFactory interface Factory { fun create(isDetailsMode: Boolean): TracingStateProvider } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt index 2850b10e9bfd6dace4933554aef3f943ec4703ab..a6e23363794e73848da3ed4cc3c54133b8e8fae1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.tracing.ui.details import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults @@ -100,6 +101,6 @@ class TracingDetailsFragmentViewModel @AssistedInject constructor( tracingRepository.refreshDiagnosisKeys() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<TracingDetailsFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsLowRiskBox.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsLowRiskBox.kt index 1b12e1f68835d26cfec5a682823518900223f032..1d4f0d58726c8575a7f51d136578daa01326b2f6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsLowRiskBox.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsLowRiskBox.kt @@ -1,7 +1,6 @@ package de.rki.coronawarnapp.tracing.ui.details.items.riskdetails import android.content.Context -import android.text.method.LinkMovementMethod import android.view.ViewGroup import androidx.annotation.LayoutRes import de.rki.coronawarnapp.R @@ -9,7 +8,6 @@ import de.rki.coronawarnapp.databinding.TracingDetailsItemRiskdetailsLowViewBind import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.tracing.ui.details.TracingDetailsAdapter import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsLowRiskBox.Item -import de.rki.coronawarnapp.util.convertToHyperlink class DetailsLowRiskBox( parent: ViewGroup, @@ -32,11 +30,6 @@ class DetailsLowRiskBox( payloads: List<Any> ) -> Unit = { item, _ -> info = item - riskDetailsInformationLowriskBodyUrl.convertToHyperlink( - context.getString(R.string.risk_details_explanation_faq_link) - ) - - riskDetailsInformationLowriskBodyUrl.movementMethod = LinkMovementMethod.getInstance() } data class Item( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt index 7e55491e6cc448f33e1626cb1b3103c3aa392f88..97a845b01ef2a4a8128585bb9e599fddf6e370ec 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/settings/SettingsTracingFragmentViewModel.kt @@ -6,13 +6,14 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient import de.rki.coronawarnapp.nearby.TracingPermissionHelper +import de.rki.coronawarnapp.storage.TracingRepository import de.rki.coronawarnapp.tracing.GeneralTracingStatus -import de.rki.coronawarnapp.tracing.ui.details.TracingDetailsItemProvider import de.rki.coronawarnapp.tracing.ui.details.items.periodlogged.PeriodLoggedBox import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.device.BackgroundModeStatus @@ -24,23 +25,20 @@ import de.rki.coronawarnapp.worker.BackgroundWorkScheduler import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.plus import timber.log.Timber class SettingsTracingFragmentViewModel @AssistedInject constructor( dispatcherProvider: DispatcherProvider, - tracingDetailsItemProvider: TracingDetailsItemProvider, tracingStatus: GeneralTracingStatus, + tracingRepository: TracingRepository, private val backgroundStatus: BackgroundModeStatus, tracingPermissionHelperFactory: TracingPermissionHelper.Factory ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val loggingPeriod: LiveData<PeriodLoggedBox.Item> = tracingDetailsItemProvider.state - .mapNotNull { items -> - items.firstOrNull { it is PeriodLoggedBox.Item } as? PeriodLoggedBox.Item - } + val loggingPeriod: LiveData<PeriodLoggedBox.Item> = tracingRepository.activeTracingDaysInRetentionPeriod + .map { PeriodLoggedBox.Item(activeTracingDaysInRetentionPeriod = it.toInt()) } .onEach { Timber.v("logginPeriod onEach") } .asLiveData(dispatcherProvider.Main) @@ -125,6 +123,6 @@ class SettingsTracingFragmentViewModel @AssistedInject constructor( object ManualCheckingDialog : Event() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SettingsTracingFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt index 27f53ae57b79ddf2ea0c09a62fed833f2125a3b4..41cd084187b9828a366899fe5bdb8a96f605d4d0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.information import android.content.Context import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.BuildConfig import de.rki.coronawarnapp.R import de.rki.coronawarnapp.nearby.ENFClient @@ -29,6 +30,6 @@ class InformationFragmentViewModel @AssistedInject constructor( context.getString(R.string.information_version).format(BuildConfig.VERSION_NAME) ).asLiveData(context = dispatcherProvider.Default) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<InformationFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModel.kt index 95d96003cd7d339b74719e4a99c84317c4dde355..4a26cc863749f88aafcf1f9bc770790657b7c11a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.interoperability import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -31,6 +32,6 @@ class InteroperabilityConfigurationFragmentViewModel @AssistedInject constructor } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<InteroperabilityConfigurationFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt index d344f94c4a8960dc5da662697f398b0646178e26..d42ce56f03bf50c7fbc47a54ec23d3b458a1f5bc 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.launcher -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.update.UpdateChecker import de.rki.coronawarnapp.util.coroutine.DispatcherProvider @@ -26,6 +27,6 @@ class LauncherActivityViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<LauncherActivityViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt index c96e28f968984a8d0b7427afa8fb84d111fd5a52..37e600e8552e1caf9e6e6b797dfefeddac2bda40 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.main -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.playbook.BackgroundNoise import de.rki.coronawarnapp.storage.LocalData @@ -65,6 +66,6 @@ class MainActivityViewModel @AssistedInject constructor( } } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<MainActivityViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt index 64ffa90ef5bec6b466d352a14469414f95d22fbb..d1d547896bf7b7eacfc357cbbb6913655044b7f4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt @@ -103,6 +103,11 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { HomeFragmentEvents.GoToContactDiary -> { context?.let { ContactDiaryActivity.start(it) } } + HomeFragmentEvents.GoToStatisticsExplanation -> { + doNavigate( + HomeFragmentDirections.actionMainFragmentToStatisticsExplanationFragment() + ) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt index bc6482d6ade46052f99373f093ccebfcdef412ae..8c7eb597923482c363d726fa01b2e4ec8754a0a6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt @@ -12,4 +12,6 @@ sealed class HomeFragmentEvents { object ShowDeleteTestDialog : HomeFragmentEvents() object GoToContactDiary : HomeFragmentEvents() + + object GoToStatisticsExplanation : HomeFragmentEvents() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt index b8962bc78d34935af5def4262a4beb5cbe6adbb2..2e8a092fe0f72fce6af47f9e030618f62e186fd2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.ui.main.home import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.notification.ShareTestResultNotificationService @@ -217,19 +218,20 @@ class HomeFragmentViewModel @AssistedInject constructor( ) { tracingItem, submissionItem, submissionState, statsData -> mutableListOf<HomeItem>().apply { when (submissionState) { - TestPositive, - SubmissionDone -> { + TestPositive, SubmissionDone -> { // Don't show risk card } else -> add(tracingItem) } + add(submissionItem) + if (statsData.isDataAvailable) { - add(StatisticsHomeCard.Item(data = statsData, onHelpAction = { })) + add(StatisticsHomeCard.Item(data = statsData, onHelpAction = { + popupEvents.postValue(HomeFragmentEvents.GoToStatisticsExplanation) + })) } - add(submissionItem) - add(DiaryCard.Item(onClickAction = { popupEvents.postValue(HomeFragmentEvents.GoToContactDiary) })) add(FAQCard.Item(onClickAction = { openFAQUrlEvent.postValue(Unit) })) @@ -300,6 +302,6 @@ class HomeFragmentViewModel @AssistedInject constructor( cwaSettings.wasDeviceTimeIncorrectAcknowledged = true } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<HomeFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentViewModel.kt index 10ff693cd92a8bfcd921353a5c0aaf6a09a0196f..73c9893a9863442047de53328cbd3cc47225477d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragmentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.onboarding import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent @@ -24,6 +25,6 @@ class OnboardingDeltaInteroperabilityFragmentViewModel @AssistedInject construct interopRepo.saveInteroperabilityUsed() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingDeltaInteroperabilityFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentViewModel.kt index 2f19367466f0a2554660609e51ba3d262522daad..9f5b9d59dc77bbae15fdee402a85aec4b64ca061 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingFragmentViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.onboarding -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -16,6 +17,6 @@ class OnboardingFragmentViewModel @AssistedInject constructor() : CWAViewModel() routeToScreen.postValue(OnboardingNavigationEvents.NavigateToEasyLanguageUrl) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsViewModel.kt index 12084c4905c1cea349366010028f428d3523774a..8597aa4d1ae7c340736e2fcebaac2cd75e2083ac 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingNotificationsViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.onboarding -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -13,6 +14,6 @@ class OnboardingNotificationsViewModel @AssistedInject constructor() : CWAViewMo completedOnboardingEvent.postValue(Unit) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingNotificationsViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyViewModel.kt index 5c8ce4237bd94e6f3c100470916dd528110d9834..f596bf7e0d24205360ec18743161f5cc066dc7ff 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingPrivacyViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.onboarding -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -16,6 +17,6 @@ class OnboardingPrivacyViewModel @AssistedInject constructor() : CWAViewModel() routeToScreen.postValue(OnboardingNavigationEvents.NavigateToOnboardingFragment) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingPrivacyViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestViewModel.kt index 2664e877c5d61cdef32dd36d8eaf37554bb3f09d..df46e8f35b55e63a7add5d2d04b309b436784cbe 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTestViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.onboarding -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -16,6 +17,6 @@ class OnboardingTestViewModel @AssistedInject constructor() : CWAViewModel() { routeToScreen.postValue(OnboardingNavigationEvents.NavigateToOnboardingTracing) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingTestViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt index 1185467f27319dbbc79b55b2091021a7908b8c60..e7df5ad46fa12363dc66dc9822209e970d53371a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingTracingFragmentViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.ui.onboarding import android.app.Activity import android.content.Intent import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient @@ -88,7 +89,7 @@ class OnboardingTracingFragmentViewModel @AssistedInject constructor( tracingPermissionHelper.handleActivityResult(requestCode, resultCode, data) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<OnboardingTracingFragmentViewModel> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt index ef39ecc6f523788403e31783b56b78c8f8dfc04f..4d413a1f0c31b1527129448c84c1bb22988eb084 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.settings import com.google.android.gms.common.api.ApiException -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient @@ -56,6 +57,6 @@ class SettingsResetViewModel @AssistedInject constructor( private val TAG: String? = SettingsResetFragment::class.simpleName } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SettingsResetViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/backgroundpriority/SettingsBackgroundPriorityFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/backgroundpriority/SettingsBackgroundPriorityFragmentViewModel.kt index 1a02b2504fdfcc02dfb19930ec8d3fe5d497c457..e29021b45dc9710e680fc26d7505b848025d9a14 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/backgroundpriority/SettingsBackgroundPriorityFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/backgroundpriority/SettingsBackgroundPriorityFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.settings.backgroundpriority import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.device.BackgroundModeStatus import de.rki.coronawarnapp.util.viewmodel.CWAViewModel @@ -21,6 +22,6 @@ class SettingsBackgroundPriorityFragmentViewModel @AssistedInject constructor( } .asLiveData(dispatcherProvider.Default) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SettingsBackgroundPriorityFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragmentViewModel.kt index 43e2f74b56742209c7427fcb7ac97a2c26e48718..bde6cae7ee4ef3a8804ba915ba0101c6d568a816 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/notifications/NotificationSettingsFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.settings.notifications import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -38,6 +39,6 @@ class NotificationSettingsFragmentViewModel @AssistedInject constructor( notificationSettings.toggleNotificationsTestEnabled() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<NotificationSettingsFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModel.kt index e34a1a618da082c975a691ca5d6001169e53fc60..0cd34185c056d102259086a7ad73fcb15e186920 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.settings.start import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.tracing.GeneralTracingStatus import de.rki.coronawarnapp.ui.settings.notifications.NotificationSettings import de.rki.coronawarnapp.util.coroutine.DispatcherProvider @@ -42,6 +43,6 @@ class SettingsFragmentViewModel @AssistedInject constructor( .map { SettingsBackgroundState((it)) } .asLiveData(dispatcherProvider.Default) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SettingsFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt index a6a7bd6ae916d15033a6697735122faa36fbbdad..18b1e1c9ce29c4e5ae593ee333f19450320cccc9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionDispatcherFragment.kt @@ -52,7 +52,7 @@ class SubmissionDispatcherFragment : Fragment(R.layout.fragment_submission_dispa override fun onResume() { super.onResume() - binding.submissionDispatcherRoot.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) + binding.submissionDispatcherRoot.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } private fun setButtonOnClickListener() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt index d88c33332bcad1439ff1b5f4e3b48228fe88ffe9..31fd005d0b24d35c4e741812dacfba27387e091a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.submission.qrcode.consent import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents @@ -34,6 +35,6 @@ class SubmissionConsentViewModel @AssistedInject constructor( routeToScreen.postValue(SubmissionNavigationEvents.NavigateToDataPrivacy) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionConsentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt index f90824a82de035cbc09cc81fec9986e3e05ff305..7f4284c4795886d4dd46e7e92532e125332c2b7d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.submission.qrcode.info -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -18,6 +19,6 @@ class SubmissionQRCodeInfoFragmentViewModel @AssistedInject constructor() : CWAV navigateToQRScan.postValue(Unit) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionQRCodeInfoFragmentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt index 75863926bc1fc65df92c585bc7c3e8fda6abbd06..e1d14baf17b2e2fd97352f09b28b2943dd2276d4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt @@ -132,10 +132,7 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( requireActivity(), R.string.submission_error_dialog_web_generic_error_title, - getString( - R.string.submission_error_dialog_web_generic_network_error_body, - exception.statusCode - ), + R.string.submission_error_dialog_web_generic_network_error_body, R.string.submission_error_dialog_web_generic_error_button_positive, null, true, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt index 37b4f7c72f38c01c72b7f87a900d0ea47683f099..5c5743d41b50737bc01670dc7035b1c8cc5e48ef 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan import androidx.lifecycle.MutableLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.TransactionException @@ -97,6 +98,6 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor( routeToScreen.postValue(SubmissionNavigationEvents.NavigateToDispatcher) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionQRCodeScanViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt index 0fa61081e177ce16a2f72c62af52d432083e3c04..bb90f61cda5dbd7f6be5cf25883fd72699df0098 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableViewModel.kt @@ -5,7 +5,8 @@ import android.content.Intent import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.submission.SubmissionRepository @@ -109,6 +110,6 @@ class SubmissionTestResultAvailableViewModel @AssistedInject constructor( tekHistoryUpdater.handleActivityResult(requestCode, resultCode, data) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultAvailableViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt index 6bdeb9959c75460666cd76bdf5938036d9c8fd5b..a59d4a698ed5115aa0b9b6cb19749983ef196860 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultready/SubmissionResultReadyViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.submission.resultready import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.submission.auto.AutoSubmission import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents import de.rki.coronawarnapp.util.coroutine.DispatcherProvider @@ -41,6 +42,6 @@ class SubmissionResultReadyViewModel @AssistedInject constructor( autoSubmission.updateLastSubmissionUserActivity() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionResultReadyViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt index e8fc814d481acadd48da09bd35ed1b9186be30de..bf7306dae8e383771de71a6040e67caba25b5268 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt @@ -2,8 +2,9 @@ package de.rki.coronawarnapp.ui.submission.symptoms.calendar import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.Symptoms @@ -99,7 +100,7 @@ class SubmissionSymptomCalendarViewModel @AssistedInject constructor( autoSubmission.updateLastSubmissionUserActivity() } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<SubmissionSymptomCalendarViewModel> { fun create(symptomIndication: Symptoms.Indication): SubmissionSymptomCalendarViewModel diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt index 363e91b4b883c1d8c717e4994e9df2d7fa7b8a64..98897e6cd9243e646666bebd38ce194dc5a13209 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.submission.symptoms.introduction import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.Symptoms import de.rki.coronawarnapp.submission.auto.AutoSubmission @@ -99,6 +100,6 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( autoSubmission.updateLastSubmissionUserActivity() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionSymptomIntroductionViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt index 62f360997e7f2535fa69a6d77c3dfe9ba5cd696f..38084f8c595d2a57130620738ce40405354a533e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt @@ -102,10 +102,7 @@ class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoIn is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( requireActivity(), R.string.submission_error_dialog_web_generic_error_title, - getString( - R.string.submission_error_dialog_web_generic_network_error_body, - exception.statusCode - ), + R.string.submission_error_dialog_web_generic_network_error_body, R.string.submission_error_dialog_web_generic_error_button_positive, null, true, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt index 86d569dda52f00215fdd75863813065c96bd367b..84b91e5b5d1600042d7f7fbfa33b28ce305570e6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.submission.tan import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.TransactionException import de.rki.coronawarnapp.exception.http.CwaWebException @@ -78,6 +79,6 @@ class SubmissionTanViewModel @AssistedInject constructor( val isCorrectLength: Boolean = false ) - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTanViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt index ced591754ee8dcbb557a25d7a362551ceb1ef91d..870df2853cfdc629cf13f2873ec5cd39a39519ad 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.ui.submission.testresult.invalid import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState @@ -46,6 +47,6 @@ class SubmissionTestResultInvalidViewModel @AssistedInject constructor( testResultAvailableNotificationService.cancelTestResultAvailableNotification() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultInvalidViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt index f5bf911c7ad2feb93912cf8734bd2ea015c359a5..b1d734482f18730dee076c93430059264ab5f0d9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.ui.submission.testresult.negative import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState @@ -45,7 +46,7 @@ class SubmissionTestResultNegativeViewModel @AssistedInject constructor( testResultAvailableNotificationService.cancelTestResultAvailableNotification() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultNegativeViewModel> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt index 38721c989499cdeda4c57cd2380797df07d6a146..a41b3c46567c7bb4f5bd73cf1e3f6515374a63ff 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt @@ -137,10 +137,7 @@ class SubmissionTestResultPendingFragment : Fragment(R.layout.fragment_submissio private fun buildErrorDialog(exception: CwaWebException) = DialogHelper.DialogInstance( requireActivity(), R.string.submission_error_dialog_web_generic_error_title, - getString( - R.string.submission_error_dialog_web_generic_network_error_body, - exception.statusCode - ), + R.string.submission_error_dialog_web_generic_network_error_body, R.string.submission_error_dialog_web_generic_error_button_positive, null, true, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt index c9ae92042ced5a132dca245e7531d36efee0e9ba..931a617e78df44f4836e5e604c99e6aefb4078f9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt @@ -3,7 +3,8 @@ package de.rki.coronawarnapp.ui.submission.testresult.pending import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.notification.ShareTestResultNotificationService import de.rki.coronawarnapp.submission.SubmissionRepository @@ -122,7 +123,7 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor( ) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultPendingViewModel> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt index 91e793734790043565ec87e14f43992153e4506a..43592aca074da95e7fca9d478207f9508dd9875a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.submission.testresult.positive import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.auto.AutoSubmission @@ -71,6 +72,6 @@ class SubmissionTestResultConsentGivenViewModel @AssistedInject constructor( autoSubmission.updateLastSubmissionUserActivity() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultConsentGivenViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt index 104c11d68c3105032c479762567073e41afc1ddd..ae6984fb4adc10cb12eca293448f9afbffd43ad4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt @@ -2,7 +2,8 @@ package de.rki.coronawarnapp.ui.submission.testresult.positive import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState @@ -32,6 +33,6 @@ class SubmissionTestResultNoConsentViewModel @AssistedInject constructor( testResultAvailableNotificationService.cancelTestResultAvailableNotification() } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultNoConsentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionContactViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionContactViewModel.kt index 7b6ce3290e737d5e2304f2e4003e3366853b8c4d..37840183038353ac054037063d756fed0ee4e855 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionContactViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionContactViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.submission.viewmodel -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -22,6 +23,6 @@ class SubmissionContactViewModel @AssistedInject constructor() : CWAViewModel() routeToScreen.postValue(SubmissionNavigationEvents.NavigateToTAN) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionContactViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt index 0bb7e7ff11aa0ce51665e195f36530e4b229375a..4515b7d2c42d1178304fc7900f5ab6e015fed8de 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionDispatcherViewModel.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.ui.submission.viewmodel -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -25,6 +26,6 @@ class SubmissionDispatcherViewModel @AssistedInject constructor() : CWAViewModel routeToScreen.postValue(SubmissionNavigationEvents.NavigateToConsent) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionDispatcherViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt index 1ce2aa558c0f895c5e687f2535d8c307ffe8b147..5ba83a626c3e1969c2b0a13418b12467277eb8d9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModel.kt @@ -5,7 +5,8 @@ import android.content.Intent import androidx.lifecycle.asLiveData import androidx.navigation.NavDirections import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.nearby.ENFClient @@ -98,7 +99,7 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModel @AssistedInject con tekHistoryUpdater.handleActivityResult(requestCode, resultCode, data) } - @AssistedInject.Factory + @AssistedFactory interface Factory : CWAViewModelFactory<SubmissionResultPositiveOtherWarningNoConsentViewModel> { fun create(): SubmissionResultPositiveOtherWarningNoConsentViewModel } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt index a55d1c049dc6e5ab0f5e8b0e720f0c1adfdb7d84..767af4172bef2a62a6143c426a9e10b7fc94d964 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModel.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.ui.submission.yourconsent import androidx.lifecycle.asLiveData -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.SingleLiveEvent @@ -39,6 +40,6 @@ class SubmissionYourConsentViewModel @AssistedInject constructor( clickEvent.postValue(SubmissionYourConsentEvents.GoLegal) } - @AssistedInject.Factory + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<SubmissionYourConsentViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt index 09078110d76ff7585fdef084aecf26112a8741c7..7bafedc84dc7597b0b5d06bebe9f1da504072641 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt @@ -4,12 +4,13 @@ import android.annotation.SuppressLint import android.content.Context import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository -import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysSettings import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker import de.rki.coronawarnapp.risk.storage.RiskLevelStorage +import de.rki.coronawarnapp.statistics.source.StatisticsProvider import de.rki.coronawarnapp.storage.AppDatabase import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.submission.SubmissionRepository @@ -35,8 +36,9 @@ class DataReset @Inject constructor( private val downloadDiagnosisKeysSettings: DownloadDiagnosisKeysSettings, private val riskLevelStorage: RiskLevelStorage, private val contactDiaryRepository: ContactDiaryRepository, - private var contactDiarySettings: ContactDiarySettings, - private val cwaSettings: CWASettings + private var contactDiaryPreferences: ContactDiaryPreferences, + private val cwaSettings: CWASettings, + private val statisticsProvider: StatisticsProvider ) { private val mutex = Mutex() @@ -62,12 +64,14 @@ class DataReset @Inject constructor( exposureDetectionTracker.clear() downloadDiagnosisKeysSettings.clear() riskLevelStorage.clear() - contactDiarySettings.clear() + contactDiaryPreferences.clear() cwaSettings.clear() // Clear contact diary database contactDiaryRepository.clear() + statisticsProvider.clear() + Timber.w("CWA LOCAL DATA DELETION COMPLETED.") } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt new file mode 100644 index 0000000000000000000000000000000000000000..61c9b2698cb6e8d8a9177579ac5b843890111c94 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/SpannableStringBuilder.kt @@ -0,0 +1,10 @@ +package de.rki.coronawarnapp.util + +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.URLSpan + +fun SpannableStringBuilder.urlSpan(start: Int, end: Int, value: String): SpannableStringBuilder { + setSpan(URLSpan(value), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + return this +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Views.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Views.kt index 0b70bf717794ab09bbd35bc899e5e24cdd24b24f..6becabc1215111587ca397b2c083c6b7e5dc8cf4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Views.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Views.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.util import android.text.SpannableString +import android.text.SpannableStringBuilder import android.text.Spanned import android.text.method.LinkMovementMethod import android.text.style.URLSpan @@ -54,3 +55,20 @@ fun TextView.setUrl(@StringRes textRes: Int, label: String, url: String) { } } } + +fun TextView.setUrl(@StringRes textRes: Int, @StringRes labelRes: Int, @StringRes urlRes: Int) { + val url = context.getString(urlRes) + val label = context.getString(labelRes) + context.getString(textRes).also { + val indexOf = it.indexOf(label) + if (indexOf > 0) { + setText( + SpannableStringBuilder(it).urlSpan(indexOf, indexOf + label.length, url), + TextView.BufferType.SPANNABLE + ) + movementMethod = LinkMovementMethod.getInstance() + } else { + text = it + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/coroutine/DispatcherProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/coroutine/DispatcherProvider.kt index 1e4abd60c7404b6886c2637738086c059136001b..0928ccd8f910cf4ccfed86b072e6da8ae6f99c3f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/coroutine/DispatcherProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/coroutine/DispatcherProvider.kt @@ -1,21 +1,21 @@ package de.rki.coronawarnapp.util.coroutine -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext // Need this to improve testing // Can currently only replace the main-thread dispatcher. // https://github.com/Kotlin/kotlinx.coroutines/issues/1365 @Suppress("PropertyName", "VariableNaming") interface DispatcherProvider { - val Default: CoroutineDispatcher + val Default: CoroutineContext get() = Dispatchers.Default - val Main: CoroutineDispatcher + val Main: CoroutineContext get() = Dispatchers.Main - val MainImmediate: CoroutineDispatcher + val MainImmediate: CoroutineContext get() = Dispatchers.Main.immediate - val Unconfined: CoroutineDispatcher + val Unconfined: CoroutineContext get() = Dispatchers.Unconfined - val IO: CoroutineDispatcher + val IO: CoroutineContext get() = Dispatchers.IO } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt index 793ba8478adc68b9bce2772e24dab5a12225f86e..0592cc608c90a69c09ac0247c2138548bc61a84b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt @@ -24,6 +24,7 @@ import de.rki.coronawarnapp.playbook.PlaybookModule import de.rki.coronawarnapp.receiver.ReceiverBinder import de.rki.coronawarnapp.risk.RiskModule import de.rki.coronawarnapp.service.ServiceBinder +import de.rki.coronawarnapp.statistics.StatisticsModule import de.rki.coronawarnapp.submission.SubmissionModule import de.rki.coronawarnapp.submission.task.SubmissionTaskModule import de.rki.coronawarnapp.task.TaskController @@ -45,7 +46,6 @@ import javax.inject.Singleton @Component( modules = [ AndroidSupportInjectionModule::class, - AssistedInjectModule::class, CoroutineModule::class, AndroidModule::class, ReceiverBinder::class, @@ -69,7 +69,8 @@ import javax.inject.Singleton BugReportingSharedModule::class, SerializationModule::class, WorkerBinder::class, - ContactDiaryRootModule::class + ContactDiaryRootModule::class, + StatisticsModule::class ] ) interface ApplicationComponent : AndroidInjector<CoronaWarnApplication> { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AssistedInjectModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AssistedInjectModule.kt deleted file mode 100644 index 88cab114e805f64f0e01a1ab919de97c38232a83..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/AssistedInjectModule.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.rki.coronawarnapp.util.di - -import com.squareup.inject.assisted.dagger2.AssistedModule -import dagger.Module - -@AssistedModule -@Module(includes = [AssistedInject_AssistedInjectModule::class]) -interface AssistedInjectModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterStatistics.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterStatistics.kt new file mode 100644 index 0000000000000000000000000000000000000000..8605f6a5a6f8a31b2d8261979b797112c7054eba --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterStatistics.kt @@ -0,0 +1,38 @@ +package de.rki.coronawarnapp.util.formatter + +import android.content.Context +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.contactdiary.util.getLocale +import de.rki.coronawarnapp.statistics.IncidenceStats +import de.rki.coronawarnapp.statistics.InfectionStats +import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.SevenDayRValue +import de.rki.coronawarnapp.statistics.StatsItem +import org.joda.time.LocalDate +import org.joda.time.format.DateTimeFormat + +fun StatsItem.getPrimaryLabel(context: Context): String { + val today = LocalDate() + val yesterday = today.minusDays(1) + val day = LocalDate(updatedAt) + val dateTimeFormatter = DateTimeFormat.mediumDate().withLocale(context.getLocale()) + + return when (this) { + is InfectionStats, + is KeySubmissionsStats -> when (day) { + today -> context.getString(R.string.statistics_primary_value_today) + yesterday -> context.getString(R.string.statistics_primary_value_yesterday) + else -> dateTimeFormatter.print(day) + } + is IncidenceStats -> when (day) { + today -> context.getString(R.string.statistics_primary_value_until_today) + yesterday -> context.getString(R.string.statistics_primary_value_until_yesterday) + else -> context.getString(R.string.statistics_primary_value_until, dateTimeFormatter.print(day)) + } + is SevenDayRValue -> when (day) { + today -> context.getString(R.string.statistics_primary_value_current) + yesterday -> context.getString(R.string.statistics_primary_value_yesterday) + else -> context.getString(R.string.statistics_primary_value_until, dateTimeFormatter.print(day)) + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/InvalidSignatureException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/InvalidSignatureException.kt new file mode 100644 index 0000000000000000000000000000000000000000..b5c532f3d96026e2350f213eb6ef7de02c7c2a7f --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/InvalidSignatureException.kt @@ -0,0 +1,13 @@ +package de.rki.coronawarnapp.util.security + +import de.rki.coronawarnapp.exception.reporting.ReportedException + +open class InvalidSignatureException( + code: Int, + message: String, + cause: Throwable? = null +) : ReportedException( + code = code, + message = message, + cause = cause +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/LiveDataExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/LiveDataExtensions.kt index e1d652cd060853b2ced95dcce8c5be835e7b7347..e984d9221e15a6f930e94fa3e78bc5f0d92f8012 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/LiveDataExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/LiveDataExtensions.kt @@ -4,7 +4,6 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.Observer -import androidx.lifecycle.observe fun <T> LiveData<T>.observe2(fragment: Fragment, callback: (T) -> Unit) { observe(fragment.viewLifecycleOwner) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModelFactoryProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModelFactoryProvider.kt index 836fe2688145ef18cd97a9a24894d686e1ce0a86..225a26f9761cee62cfbaf343711872ed16934ab7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModelFactoryProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModelFactoryProvider.kt @@ -5,24 +5,25 @@ import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.savedstate.SavedStateRegistryOwner -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject /** * Using fragments as example: * - * Step 1: Inject CWAViewModelSource.Factory into Fragment + * Step 1: Inject [CWAViewModelFactoryProvider.Factory] into Fragment * - * Step 2: Use assisted inject `CWAViewModelSource.Factory.create(...)` to create an instance of - * CWAViewModelSource that has access to the fragments `savedStateOwner` + * Step 2: Use assisted inject [CWAViewModelFactoryProvider.Factory.create] to create an instance of + * [CWAViewModelFactoryProvider] that has access to the fragments `savedStateOwner` * * Step 3: On first access (like "by lazy") to the viewModel the extension function, * i.e. `ourViewModel.onActionClicked`, - * `cwaViewModelsAssisted` (or one of it's variants) checks the fragments viewmodel store + * [cwaViewModelsAssisted] (or one of it's variants) checks the fragments viewmodel store * * Step 4: If no viewmodel was available, this factory is tasked with creating a new viewmodel * - * Step 5: `CWAViewModelSource.create` is called as part of that flow and returns a new viewmodel + * Step 5: [CWAViewModelFactoryProvider.create] is called as part of that flow and returns a new viewmodel * * Step 6: The new viewmodel is cached and returned * @@ -38,6 +39,7 @@ class CWAViewModelFactoryProvider @AssistedInject constructor( /** * Called indirectly (lazy) when the viewModel is accessed in a Fragment/Activity **/ + @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create( key: String, modelClass: Class<T>, @@ -45,15 +47,13 @@ class CWAViewModelFactoryProvider @AssistedInject constructor( ): T { // Find the right factory for our ViewModel val factory = creators.entries.find { modelClass.isAssignableFrom(it.key) }?.value - if (factory == null) throw IllegalStateException("Unknown ViewModel factory: $modelClass") + ?: throw IllegalStateException("Unknown ViewModel factory: $modelClass") // If an `assistAction` was passed from `cwaViewModels` use that to create the ViewModel - @Suppress("UNCHECKED_CAST") var vm: T? = assistAction?.invoke(factory, handle) as? T // If no `assistAction` was passed, try one of the defaults // The Fragment or Activity may have used one of them to reduce boilerplate code. - @Suppress("UNCHECKED_CAST") if (vm == null) { vm = when (factory) { is SavedStateCWAViewModelFactory<*> -> factory.create(handle) as? T @@ -69,7 +69,7 @@ class CWAViewModelFactoryProvider @AssistedInject constructor( * Injected into fragments/activities * Uses assisted injection to get access to the fragments/activities SavedStateRegistryOwner */ - @AssistedInject.Factory + @AssistedFactory interface Factory { fun create( savedStateOwner: SavedStateRegistryOwner, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoiseOneTimeWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoiseOneTimeWorker.kt index 522d279d6fd6455a94e7daea9c3d0a8b9d14250f..ad5a57f87918c3dfdf5764b7f67206309c31afc7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoiseOneTimeWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoiseOneTimeWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.playbook.Playbook import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import timber.log.Timber @@ -39,7 +40,7 @@ class BackgroundNoiseOneTimeWorker @AssistedInject constructor( return result } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<BackgroundNoiseOneTimeWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoisePeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoisePeriodicWorker.kt index d1111667fe3b73b484fb089198120c81a56593b8..f66cda4621d977c92e19d16da1c1fe0b5c8cf9cd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoisePeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundNoisePeriodicWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import de.rki.coronawarnapp.worker.BackgroundWorkScheduler.stop @@ -58,7 +59,7 @@ class BackgroundNoisePeriodicWorker @AssistedInject constructor( Timber.tag(TAG).d("$id: worker stopped") } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<BackgroundNoisePeriodicWorker> companion object { 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 23199d02733d645ba7688ce839cb6008126c84c0..f087a031badd867c65e3f983792b90dcc833f00d 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 @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.common.DefaultTaskRequest @@ -51,7 +52,7 @@ class DiagnosisKeyRetrievalOneTimeWorker @AssistedInject constructor( return result } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<DiagnosisKeyRetrievalOneTimeWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt index adff8651b5bb674bad55ecbba08bbccad7b0adab..d375bd226aed66840c86fb5f591dca37de4c15d8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory import timber.log.Timber @@ -49,7 +50,7 @@ class DiagnosisKeyRetrievalPeriodicWorker @AssistedInject constructor( return result } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<DiagnosisKeyRetrievalPeriodicWorker> companion object { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt index 9dcba978e54483367305972cf1c10044beb69354..47b8b15f2257abeefe1e5f3648be753f65a82073 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import de.rki.coronawarnapp.exception.NoRegistrationTokenSetException import de.rki.coronawarnapp.notification.NotificationConstants import de.rki.coronawarnapp.notification.NotificationHelper @@ -129,7 +130,7 @@ class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor( Timber.tag(TAG).d("$id: Background worker stopped") } - @AssistedInject.Factory + @AssistedFactory interface Factory : InjectedWorkerFactory<DiagnosisTestResultRetrievalPeriodicWorker> companion object { diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_7_day_r_value.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_7_day_r_value.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e6ee577ea846ec4c8c333a075a7dada67444583 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_7_day_r_value.xml @@ -0,0 +1,101 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="74dp" + android:height="142dp" + android:viewportWidth="74" + android:viewportHeight="142"> + <path + android:fillColor="#3F3F43" + android:pathData="M55.12,20.069L54.909,25.971H60.336L59.536,20.069H55.12Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M62.162,16.17C61.699,25.247 52.666,23.622 52.888,14.505C53.349,5.429 62.384,7.051 62.162,16.17Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M50.54,50.994C46.734,66.255 50.388,88.185 51.565,105.785L54.327,105.875C54.327,105.875 55.912,77.253 56.584,70.744C56.788,68.767 57.455,63.643 57.795,61.805C57.822,61.666 58.115,61.057 58.151,61.767C58.375,66.255 60.39,72.091 60.837,77.028C61.883,88.566 61.652,96.599 62.107,106.122C62.107,106.122 65.31,108.021 65.314,108C65.984,105.343 67.777,83.452 67.777,80.17C67.777,79.497 68.001,64.684 65.091,51.218L50.54,50.994Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M69.568,52.363C67.887,60.189 79.169,61.702 70.998,50.745L69.568,52.363Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M71.842,50.365C71.423,47.403 70.689,42.874 70.015,39.772C68.896,34.61 67.553,26.755 60.166,24.735C58.925,25.935 55.997,25.148 54.999,24.735C54.981,24.728 54.974,24.735 54.954,24.735C48.133,24.802 46.463,31.414 45.391,35.283C44.272,39.323 42.353,45.345 43.217,46.729C44.896,49.423 46.358,47.982 50.381,45.159C50.573,45.024 50.768,44.882 50.965,44.741C50.902,47.452 50.777,49.896 50.542,50.994C50,53.532 65.764,53.463 65.317,51.667C64.367,47.865 63.973,40.894 63.973,35.508C63.973,35.059 65.09,37.341 65.54,38.874C66.66,42.69 67.62,49.212 69.106,52.453C69.57,53.463 70.228,53.043 71.316,52.666C72.247,52.332 71.896,50.754 71.842,50.365ZM48.525,41.792C48.525,41.792 48.749,41.568 49.644,38.425C50.17,36.581 50.987,33.712 50.987,33.712C50.987,33.712 51.041,37.115 51.016,40.952C49.324,41.466 48.525,41.792 48.525,41.792Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M54.318,104.744L51.435,104.185C51.35,104.952 50.316,106.205 50.887,109.183C50.931,109.421 50.987,111.685 51.063,111.93C51.133,112.157 52.107,112.487 52.107,112.262C52.107,112.036 51.829,109.239 52.33,110.242C52.554,110.691 52.554,112.262 53.674,113.16C54.952,114.186 58.979,114.516 57.479,112.711C55.241,110.018 54.585,105.63 54.41,104.688" /> + <path + android:fillColor="#3F3F43" + android:pathData="M51.625,4.466C51.625,4.466 52.608,2.264 54.377,2.78C54.377,2.78 55.021,-0.676 59.404,0.119C63.788,0.913 63.714,3.923 63.714,3.923C63.714,3.923 68.39,3.93 67.15,9.462C67.15,9.462 68.258,11.967 66.49,13.241C66.49,13.241 67.11,15.993 64.376,16.305C64.376,16.305 64.161,17.113 63.481,16.974C63.481,16.974 62.274,18.522 61.836,17.476C61.574,16.85 61.632,15.611 61.721,14.718C61.8,13.93 61.567,13.147 61.068,12.534C60.459,11.789 59.546,10.939 58.5,10.867C56.606,10.737 53.998,12.068 53.64,13.446C53.371,14.476 54.133,17.793 52.507,17.802C51.471,17.809 50.972,16.287 50.972,16.287C50.972,16.287 47.959,15.773 48.805,13.378C48.805,13.378 47.285,11.63 48.554,9.549C48.554,9.552 47.101,4.538 51.625,4.466Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M62.064,104.666C62.064,104.666 62.026,106.156 61.735,107.327C61.511,108.225 61.287,110.918 62.183,113.163H63.526C63.526,113.163 63.078,110.694 63.302,109.347C63.362,108.988 63.526,110.918 64.869,112.489C65.487,113.212 66.042,113.311 66.304,113.34C66.785,113.394 69.642,113.387 69.794,113.387C70.242,113.387 70.689,112.489 68.892,111.693C68.41,111.479 67.81,111.071 67.331,110.469C66.517,109.448 66.183,107.718 65.735,105.249L62.064,104.666Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M58.287,32.891L61.343,32.951C61.744,32.96 62.035,33.292 61.992,33.692L61.198,41.101C61.155,41.502 60.795,41.821 60.394,41.812L57.338,41.751C56.938,41.742 56.646,41.41 56.689,41.011L57.484,33.602C57.526,33.2 57.884,32.882 58.287,32.891Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M54.112,41.137L54.753,42.082C58.934,42.052 63.347,36.136 57.287,37.128C55.729,37.846 54.609,39.184 53.537,40.151" /> + <path + android:fillColor="#3F3F43" + android:pathData="M24.083,113.59C23.966,117.433 16.981,116.443 18.634,112.272C19.883,107.058 18.412,108.637 23.156,108.431C23.383,108.871 24.119,112.337 24.083,113.59Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M4.545,111.212C5.838,110.471 10.042,108.89 9.366,107.181L13.009,106.623C13.042,107.28 14.896,112.031 12.657,112.47C10.847,112.83 -0.588,115.896 4.545,111.212Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M25.657,53.378C25.657,53.378 26.063,57.897 26.135,58.756C26.799,66.812 24.58,101.603 23.985,106.795C23.944,107.152 23.748,108.248 23.12,108.485C21.835,108.968 19.845,108.485 19.845,108.485C19.487,108.485 19.191,108.2 19.174,107.838C18.906,102.011 16.217,68.311 15.811,65.197C14.229,70.995 13.948,100.099 13.291,107.432C13.255,107.845 12.911,108.162 12.5,108.162C12.5,108.162 10.847,108.483 10.059,108.162C9.225,107.821 9.127,106.769 9.091,106.45C7.383,91.546 4.462,67.099 6.175,52.137L12.206,54.771L25.657,53.378Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M20.889,13.575C20.889,13.575 11.585,12.936 11.58,12.945L11.403,13.534C10.859,11.75 8.74,6.773 11.47,5.199C13.166,1.148 22.614,2.983 22.313,8.366C22.42,8.998 20.889,13.575 20.889,13.575Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M2.331,49.872L1.089,49.969C0.162,52.004 -0.048,55.285 1.457,57.259C2.969,58.65 4.591,56.371 4.67,55.087C4.906,53.064 4.211,51.294 3.626,49.761L2.331,49.872Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M1.388,33.923C1.648,32.477 1.978,31.07 2.386,29.718C3.275,26.353 3.633,24.101 6.308,22.305C7.417,21.561 8.864,21.042 10.847,20.373C11.564,20.132 12.065,19.693 12.216,19.092C12.402,18.358 17.989,18.669 19.377,18.816C19.58,18.838 19.583,18.655 19.669,18.843C19.924,19.408 20.402,20.132 21.596,20.615C23.211,21.267 27.124,22.551 28.607,25.194C30.912,29.305 34.734,45.721 32.121,47.509C30.948,48.313 26.617,48.979 25.915,49.1C25.91,51.217 25.896,54.17 25.705,54.614C24.214,58.078 11.408,58.846 6.055,54.925C5.475,54.5 6.213,39.862 7.154,33.334C7.051,33.769 6.941,34.242 6.829,34.742C6.246,37.332 5.831,40.41 5.353,43.548C4.882,46.638 4.636,50.307 4.223,50.696C2.881,51.953 1.942,51.886 1.022,51.637C0.49,51.493 0.048,51.041 0.031,50.483C-0.141,44.272 0.418,39.314 1.388,33.923ZM26.082,43.845C26.851,43.548 27.188,43.688 27.568,43.548C27.623,41.74 25.872,34.305 25.657,33.651C25.657,33.682 25.946,38.58 26.082,43.845Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M15.655,39.847H18.942C19.37,39.847 19.719,40.197 19.719,40.632V48.721C19.719,49.153 19.372,49.506 18.942,49.506H15.715C15.288,49.506 14.939,49.155 14.939,48.721V40.641C14.939,40.204 15.283,39.849 15.708,39.849" /> + <path + android:fillColor="#3F3F43" + android:pathData="M16.004,21.124C14.545,20.832 12.932,19.929 12.249,19.021C12.474,18.171 12.837,16.906 12.811,16.737C13.178,17.136 18.825,17.146 19.198,16.737C19.172,16.906 19.535,18.171 19.759,19.021C19.071,19.929 17.464,20.829 16.004,21.124Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M20.911,13.776V13.669C21.644,13.153 22.105,12.219 22.081,11.504C22.084,11.125 21.933,10.224 21.515,10.62C21.405,11.103 21.045,12.786 20.686,13.054C20.987,2.529 14.268,12.257 12.369,7.815C10.957,8.293 12.18,12.373 11.717,13.039C11.334,12.762 11.084,11.118 10.986,10.62C10.9,10.555 10.821,10.502 10.754,10.529C9.978,11.369 10.613,12.976 11.595,13.674C11.523,16.25 14.456,19.236 16.253,18.845C18.039,19.231 20.939,16.305 20.911,13.727" /> + <path + android:fillColor="#E4B489" + android:pathData="M20.641,12.678C20.643,12.579 20.646,12.487 20.646,12.393C20.648,12.497 20.648,12.603 20.648,12.711C20.646,12.699 20.643,12.69 20.641,12.678Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M23.06,46.848L23.072,45.684C21.233,44.624 18.161,44.149 16.176,45.433C14.741,46.773 16.754,48.523 17.958,48.704C19.845,49.1 21.567,48.578 23.063,48.141" /> + <path + android:fillColor="#F6B893" + android:pathData="M33.957,27.638L33.277,34.412L31.896,35.505L30.515,36.324L32.449,40.423L43.223,40.696L41.841,36.324L42.118,34.138L41.275,33.772C40.598,33.477 40.123,32.857 40.024,32.13L39.408,27.635H33.957V27.638Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M42.648,22.892C42.076,33.943 30.929,31.964 31.202,20.865C31.772,9.814 42.922,11.79 42.648,22.892Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M41.898,24.024C39.271,24.856 36.27,25.585 30.642,24.024C29.861,24.891 29.517,27.457 32.518,28.705C35.52,29.953 39.084,29.225 40.491,28.705C42.054,26.798 44.524,23.192 41.898,24.024Z" + android:strokeWidth="1" + android:strokeColor="#ffffff" /> + <path + android:fillColor="#8C8C98" + android:pathData="M28.304,65.29C23.608,83.871 28.116,110.571 29.569,131.999L32.979,132.108C32.979,132.108 34.935,97.26 35.763,89.336C36.015,86.929 36.838,81.784 37.258,79.546C37.291,79.376 37.653,78.636 37.697,79.499C37.973,84.964 40.46,90.976 41.012,96.987C42.303,111.035 42.018,120.815 42.579,132.409C42.579,132.409 46.532,134.721 46.538,134.696C47.364,131.461 49.576,104.808 49.576,100.813C49.576,99.993 50.654,82.923 46.261,65.563L28.304,65.29Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M50.842,66.538C48.248,75.941 62.053,78.526 52.715,64.667L50.842,66.538Z" /> + <path + android:fillColor="#C66A61" + android:pathData="M19.268,60.098C21.34,63.377 23.144,61.622 28.108,58.185C28.346,58.021 28.584,57.849 28.83,57.677C28.752,60.978 28.597,63.953 28.307,65.289C27.639,68.38 47.093,68.295 46.541,66.109C45.369,61.48 44.883,52.993 44.883,46.435C44.883,45.888 45.806,48.643 46.264,50.534C47.43,55.324 48.278,62.3 50.113,66.246C50.685,67.475 51.497,66.964 52.84,66.505C53.989,66.098 53.525,64.18 53.489,63.702C53.229,60.242 52.619,55.4 51.787,51.624C50.511,45.812 49.549,37.194 42.115,33.986C41.993,33.934 41.853,34.024 41.855,34.158C41.927,36.609 39.3,40.147 37.974,40.147C35.487,40.147 31.344,36.595 30.769,33.726C24.854,35.434 23.097,42.079 21.951,46.159C20.569,51.08 18.202,58.412 19.268,60.098ZM26.012,53.687C26.205,53.214 26.564,52.19 27.202,49.987L28.515,45.449C28.573,45.246 28.874,45.285 28.876,45.495C28.893,47.069 29.197,48.509 29.175,51.624C27.788,52.739 26.719,53.753 26.247,53.925C26.094,53.982 25.951,53.835 26.012,53.687Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M33.076,130.468L29.41,130.053C29.305,130.988 28.028,132.512 28.733,136.138C28.788,136.428 28.857,139.185 28.951,139.483C29.037,139.759 30.239,140.161 30.239,139.887C30.239,139.611 29.896,136.207 30.515,137.428C30.791,137.975 30.791,139.887 32.172,140.98C33.75,142.229 38.72,142.631 36.869,140.434C34.106,137.155 33.28,131.955 33.065,130.807" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M42.537,130.647C42.537,130.647 42.479,132.45 42.117,133.876C41.841,134.969 41.565,138.248 42.67,140.981H44.327C44.327,140.981 43.775,137.975 44.051,136.336C44.126,135.899 44.327,138.248 45.985,140.161C46.748,141.041 47.433,141.161 47.756,141.197C48.35,141.262 51.875,141.254 52.063,141.254C52.615,141.254 53.168,140.161 50.95,139.191C50.356,138.932 49.615,138.434 49.024,137.702C48.018,136.459 47.557,134.355 47.005,131.349L42.537,130.647Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M33.541,50.633L34.668,51.465C39.535,49.76 42.299,41.192 35.631,44.754C34.103,46.202 33.335,48.188 32.474,49.729" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M44.646,35.448C44.256,35.155 43.936,34.942 43.499,34.688C42.994,34.393 42.637,34.232 42.079,34C42.035,33.98 41.933,34 41.902,34.057C41.433,33.942 41.06,33.735 40.67,33.376C39.85,32.622 39.325,31.283 39.601,29.917C39.75,29.179 40.038,28.873 40.195,28.685C43.745,24.452 41.408,17.716 41.408,17.716C41.408,17.716 39.413,21.875 31.888,24.723C31.888,24.723 32.2,26.657 33.305,28.024C33.385,28.125 34.214,28.865 34.244,30.026C34.272,31.166 34.275,32.319 33.487,34.065C32.529,36.196 30.089,36.71 28.238,35.978C26.857,35.431 26.305,34.065 26.241,32.718C26.241,32.718 30.007,32.937 29.12,28.778C28.457,25.652 27.667,18.823 30.786,14.875C30.83,14.82 30.874,14.765 30.918,14.711C30.948,14.67 30.985,14.629 31.02,14.59C31.084,14.517 31.15,14.443 31.216,14.372C31.28,14.306 31.344,14.241 31.407,14.175C32.385,13.194 33.673,12.456 35.355,12.096C35.355,12.096 41.775,11.221 42.88,14.943C42.88,14.943 44.651,15.82 44.875,19.104C45.096,22.389 44.541,23.591 44.43,26.111C44.298,29.174 44.209,32.243 47.53,32.898C47.535,32.893 47.652,34.565 44.646,35.448Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_illustration_statistics_explanation.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_illustration_statistics_explanation.xml new file mode 100644 index 0000000000000000000000000000000000000000..a61e6168400131da501d64ea502910d833ca09f5 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_illustration_statistics_explanation.xml @@ -0,0 +1,37 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="234dp" + android:height="148dp" + android:viewportWidth="234" + android:viewportHeight="148"> + <path + android:pathData="M142.346,145.276H92.238C87.136,145.274 83.001,141.138 83,136.036L83.013,11.057C83.02,5.96 87.154,1.832 92.251,1.832L142.36,1.833C147.453,1.842 151.577,5.972 151.58,11.066V136.047C151.578,141.145 147.445,145.277 142.346,145.276Z" + android:fillColor="#232324" + android:fillType="evenOdd"/> + <path + android:pathData="M142.346,145.276H92.238C87.136,145.274 83.001,141.138 83,136.036L83.013,11.057C83.02,5.96 87.154,1.832 92.251,1.832L142.36,1.833C147.453,1.842 151.577,5.972 151.58,11.066V136.047C151.578,141.145 147.445,145.277 142.346,145.276Z" + android:strokeWidth="3.567" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#4A4A4A"/> + <path + android:pathData="M92.815,11L141.357,11A4,4 0,0 1,145.357 15L145.357,24A4,4 0,0 1,141.357 28L92.815,28A4,4 0,0 1,88.815 24L88.815,15A4,4 0,0 1,92.815 11z" + android:fillColor="#3F3F43"/> + <path + android:pathData="M92.815,36L141.815,36A4,4 0,0 1,145.815 40L145.815,70A4,4 0,0 1,141.815 74L92.815,74A4,4 0,0 1,88.815 70L88.815,40A4,4 0,0 1,92.815 36z" + android:fillColor="#3F3F43"/> + <path + android:pathData="M92.815,113L141.815,113A4,4 0,0 1,145.815 117L145.815,135A4,4 0,0 1,141.815 139L92.815,139A4,4 0,0 1,88.815 135L88.815,117A4,4 0,0 1,92.815 113z" + android:fillColor="#3F3F43"/> + <path + android:pathData="M92.815,79L118.815,79A4,4 0,0 1,122.815 83L122.815,104A4,4 0,0 1,118.815 108L92.815,108A4,4 0,0 1,88.815 104L88.815,83A4,4 0,0 1,92.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M129.815,79L155.815,79A4,4 0,0 1,159.815 83L159.815,104A4,4 0,0 1,155.815 108L129.815,108A4,4 0,0 1,125.815 104L125.815,83A4,4 0,0 1,129.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M166.815,79L192.815,79A4,4 0,0 1,196.815 83L196.815,104A4,4 0,0 1,192.815 108L166.815,108A4,4 0,0 1,162.815 104L162.815,83A4,4 0,0 1,166.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M203.815,79L229.815,79A4,4 0,0 1,233.815 83L233.815,104A4,4 0,0 1,229.815 108L203.815,108A4,4 0,0 1,199.815 104L199.815,83A4,4 0,0 1,203.815 79z" + android:fillColor="#007FAD"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_info.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..42cd7a492eb31e144834132e816039281ef43e26 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_info.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="21" + android:viewportHeight="21"> + <path + android:fillColor="#83D2F2" + android:pathData="M10.5,20.8813C16.1294,20.8813 20.7847,16.2261 20.7847,10.5967C20.7847,4.957 16.1294,0.312 10.4897,0.312C4.8604,0.312 0.2153,4.957 0.2153,10.5967C0.2153,16.2261 4.8706,20.8813 10.5,20.8813ZM10.5,19.5483C5.5371,19.5483 1.5483,15.5493 1.5483,10.5967C1.5483,5.6338 5.5371,1.6348 10.4897,1.6348C15.4526,1.6348 19.4517,5.6338 19.4619,10.5967C19.4619,15.5493 15.4629,19.5483 10.5,19.5483ZM10.4385,6.731C11.0742,6.731 11.5664,6.2388 11.5664,5.6133C11.5664,4.9878 11.0742,4.4854 10.4385,4.4854C9.813,4.4854 9.3105,4.9878 9.3105,5.6133C9.3105,6.2388 9.813,6.731 10.4385,6.731ZM8.6645,16.1953H12.8687C13.1968,16.1953 13.4531,15.9492 13.4531,15.6313C13.4531,15.3135 13.1968,15.0571 12.8687,15.0571H11.3921V9.3149C11.3921,8.8945 11.1768,8.6074 10.7769,8.6074H8.7978C8.4697,8.6074 8.2134,8.8638 8.2134,9.1714C8.2134,9.4995 8.4697,9.7456 8.7978,9.7456H10.1411V15.0571H8.6645C8.3364,15.0571 8.0801,15.3135 8.0801,15.6313C8.0801,15.9492 8.3364,16.1953 8.6645,16.1953Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_infection.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_infection.xml new file mode 100644 index 0000000000000000000000000000000000000000..aec3b8d439a269a9cce2d7cecce0d0ba6ad32f46 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_infection.xml @@ -0,0 +1,42 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="134dp" + android:height="157dp" + android:viewportWidth="134" + android:viewportHeight="157"> + <group> + <clip-path + android:pathData="M14,0L120,0A14,14 0,0 1,134 14L134,143A14,14 0,0 1,120 157L14,157A14,14 0,0 1,0 143L0,14A14,14 0,0 1,14 0z"/> + <path + android:pathData="M173.474,68.276C178.436,94.254 161.38,119.289 135.396,124.249C109.412,129.21 84.37,112.155 79.407,86.177C74.47,60.231 91.501,35.164 117.485,30.204C143.437,25.269 168.511,42.298 173.474,68.276Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M143.046,144.351C141.155,144.974 134.513,147.251 136.933,139.931C140.126,130.388 132.52,124.874 132.52,124.874L126.539,121.336L147.987,117.65C147.987,117.65 143.62,122.526 143.236,126.41C143.041,128.38 143.832,134.13 145.964,135.961C148.095,137.792 152.315,141.332 143.046,144.351Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M76.868,37.373C78.673,35.469 80.404,32.951 82.25,37.291C82.887,38.807 83.758,40.033 84.77,41.043C87.299,43.698 89.522,44.471 93.186,44.512L96.014,44.544L85.39,54.658C85.39,54.658 86.007,51.262 83.109,48.796C81.926,47.766 79.562,45.187 77.381,45.315C75.384,45.348 70.271,44.334 76.868,37.373Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M87.032,86.959C85.637,87.032 84.308,86.274 83.639,85.043C82.528,82.866 81.054,79.32 74.652,78.968C67.363,78.544 58.933,80.532 58.375,80.873C54.146,83.511 51.511,85.349 50.989,75.961C50.498,67.068 53.025,65.421 56.951,69.506C57.805,70.382 58.887,71.025 60.065,71.332C64.297,72.477 77.916,73.881 83.201,69.831C84.814,68.6 89.557,78.207 89.557,78.207C90.883,80.523 87.032,86.959 87.032,86.959Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M69.257,117.024C66.05,111.952 66.224,108.131 72.71,107.742C83.913,107.022 86.267,93.167 86.267,93.167L84.385,87.611L98.83,109.208C98.83,109.208 92.184,107.442 87.7,108.521C85.505,109.022 80.116,112.222 78.492,115.07C76.76,118.108 73.248,123.34 69.257,117.024Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M122.198,118.62C122.198,118.62 114.011,117.718 111.489,127.453C109.024,137.194 108.847,139.513 108.847,139.513C108.847,139.513 112,153.965 100.086,150.012C88.172,146.059 91.019,140.578 97.981,137.268C97.981,137.268 107.415,124.16 104.214,115.97C101.014,107.78 101.274,110.322 101.274,110.322L124.261,110.132L122.198,118.62Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M151.026,14.408C152.952,15.002 159.66,16.977 153.424,21.525C145.295,27.474 148.272,36.361 148.272,36.361L151.077,42.687L131.525,33.245C131.525,33.245 137.9,31.803 140.455,28.888C141.72,27.414 144.471,22.269 143.743,19.529C143.039,16.821 141.766,11.507 151.026,14.408Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + <path + android:pathData="M103.455,38.776C103.455,38.776 110.746,35.619 107.968,26.004C106.642,21.406 104.568,16.521 103.957,14.777C103.473,13.451 102.772,12.245 101.792,11.209C100.066,9.367 97.977,5.635 106.716,3.972C116.876,2.007 116.005,5.605 114.382,8.452C113.732,9.592 113.38,10.909 113.289,12.227C113.03,16.789 114.243,28.739 119.374,32.903C126.168,38.44 124.671,36.364 124.671,36.364L108.17,46.849L103.455,38.776Z" + android:fillColor="#3F3F43" + android:fillType="evenOdd"/> + </group> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_warnende_personen.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_warnende_personen.xml new file mode 100644 index 0000000000000000000000000000000000000000..79e5c2e1e7bda49c364706d969f65ad982d73fec --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_main_illustration_warnende_personen.xml @@ -0,0 +1,309 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="128dp" + android:height="147dp" + android:viewportWidth="128" + android:viewportHeight="147"> + <group> + <clip-path + android:pathData="M0,0h128v147h-128z"/> + <path + android:pathData="M46.685,102.15C45.72,95.03 44.955,87.9 43.761,80.793C41.285,66.012 38.142,51.354 35.299,36.651C27.719,36.011 22.423,35.604 22.423,35.604C22.423,35.604 8.44,34.01 8.54,40.376C8.64,46.743 24.444,49.273 24.444,49.273L26.741,49.736L27.949,57.484C27.949,57.484 15.694,73.705 16.794,78.612C17.893,83.518 25.456,81.016 25.456,81.016C25.456,81.016 21.561,94.646 25.171,98.031C27.821,100.518 30.33,99.261 30.33,99.261C30.33,99.261 24.183,105.354 27.31,110.406C28.684,112.622 36.183,112.947 36.183,112.947L48.23,112.031C47.187,108.804 46.848,105.49 46.685,102.15Z" + android:fillColor="#F6B893" + android:fillType="evenOdd"/> + <path + android:pathData="M97.324,130.774L52.842,139.053C48.307,139.901 43.953,136.904 43.102,132.381L22.334,21.438C21.481,16.906 24.477,12.557 29.001,11.71L73.484,3.431C78.018,2.582 82.372,5.58 83.223,10.102L103.991,121.046C104.844,125.578 101.86,129.935 97.324,130.774Z" + android:fillColor="#232324"/> + <path + android:pathData="M51.739,140.804C49.59,140.899 47.482,140.303 45.679,139.069C43.485,137.567 42.007,135.292 41.514,132.688L20.745,21.744C19.739,16.349 23.31,11.143 28.708,10.142L73.191,1.862C75.808,1.38 78.458,1.936 80.651,3.438C82.844,4.939 84.322,7.214 84.816,9.819L105.585,120.762C106.591,126.158 103.03,131.362 97.621,132.365L53.137,140.634C52.666,140.727 52.199,140.777 51.739,140.804ZM74.734,4.908C74.42,4.924 74.099,4.963 73.791,5.02L29.309,13.299C25.656,13.982 23.24,17.504 23.925,21.144L44.693,132.088C45.027,133.851 46.022,135.388 47.508,136.406C48.994,137.425 50.783,137.806 52.548,137.475L97.03,129.195C100.683,128.513 103.098,124.991 102.414,121.35L81.645,10.407C81.312,8.643 80.317,7.107 78.831,6.088C77.614,5.24 76.192,4.844 74.734,4.908Z" + android:fillColor="#4A4A4A"/> + <path + android:pathData="M204.434,111.192L149.953,99.926C149.953,99.926 136.627,95.689 132.125,84.147C128.107,73.84 112.942,49.132 107.13,41.989C104.698,37.486 100.85,31.246 97.018,28.559C96.013,27.857 95.242,26.888 94.816,25.761C93.395,22 90.329,14.112 89.477,14.2C88.364,14.306 82.804,14.877 81.943,21.929C81.081,28.98 83.9,41.453 91.014,44.273C91.014,44.273 91.386,45.672 92.029,48.075C95.699,67.475 105.079,117.939 105.117,118.123C106.301,124.124 105.491,124.828 104.715,127.066C118.072,129.717 204.779,167.652 204.779,167.652L204.885,136.452L204.434,111.192Z" + android:fillColor="#F6B893" + android:fillType="evenOdd"/> + <path + android:pathData="M58.197,33.733L58.554,35.857C58.972,35.932 59.318,36.257 59.392,36.698C59.486,37.257 59.105,37.786 58.545,37.88C57.982,37.974 57.452,37.596 57.358,37.038C57.284,36.596 57.505,36.177 57.876,35.97L57.519,33.846C56.637,33.92 55.788,33.708 55.072,33.285L53.815,35.041C54.058,35.386 54.071,35.86 53.811,36.224C53.478,36.685 52.836,36.792 52.371,36.465C51.908,36.135 51.801,35.495 52.131,35.033C52.394,34.67 52.847,34.528 53.257,34.644L54.514,32.888C53.878,32.352 53.402,31.621 53.184,30.77L51.047,31.126C50.974,31.543 50.646,31.888 50.204,31.962C49.642,32.055 49.111,31.678 49.017,31.119C48.924,30.561 49.302,30.032 49.864,29.938C50.307,29.864 50.729,30.083 50.934,30.453L53.071,30.097C52.998,29.221 53.21,28.376 53.636,27.662L51.874,26.41C51.524,26.653 51.05,26.666 50.683,26.408C50.22,26.078 50.112,25.438 50.442,24.976C50.775,24.516 51.417,24.409 51.882,24.736C52.247,24.996 52.39,25.448 52.272,25.853L54.035,27.104C54.573,26.472 55.306,25.997 56.165,25.78L55.808,23.656C55.39,23.581 55.044,23.256 54.97,22.814C54.876,22.256 55.254,21.727 55.817,21.633C56.377,21.539 56.91,21.916 57.003,22.475C57.078,22.916 56.856,23.336 56.486,23.542L56.842,25.667C57.844,25.583 58.804,25.867 59.575,26.415L64.366,19.729C61.791,17.9 58.512,17.051 55.142,17.614C48.402,18.739 43.85,25.088 44.977,31.794C46.103,38.5 52.479,43.024 59.22,41.899C62.59,41.336 65.412,39.469 67.25,36.902L60.536,32.138C59.986,32.907 59.171,33.487 58.197,33.733Z"> + <aapt:attr name="android:fillColor"> + <gradient + android:startY="17.9124" + android:startX="53.3534" + android:endY="42.2011" + android:endX="57.4092" + android:type="linear"> + <item android:offset="0" android:color="#FFBDE0F9"/> + <item android:offset="0.003906" android:color="#FFBCE0F9"/> + <item android:offset="0.007812" android:color="#FFBCE0F9"/> + <item android:offset="0.011719" android:color="#FFBBDFF8"/> + <item android:offset="0.015625" android:color="#FFBADFF8"/> + <item android:offset="0.019531" android:color="#FFB9DFF8"/> + <item android:offset="0.023437" android:color="#FFB9DFF8"/> + <item android:offset="0.027344" android:color="#FFB8DEF8"/> + <item android:offset="0.03125" android:color="#FFB7DEF8"/> + <item android:offset="0.035156" android:color="#FFB6DEF7"/> + <item android:offset="0.039062" android:color="#FFB6DEF7"/> + <item android:offset="0.042969" android:color="#FFB5DDF7"/> + <item android:offset="0.046875" android:color="#FFB4DDF7"/> + <item android:offset="0.050781" android:color="#FFB3DDF7"/> + <item android:offset="0.054687" android:color="#FFB3DDF7"/> + <item android:offset="0.058594" android:color="#FFB2DCF7"/> + <item android:offset="0.0625" android:color="#FFB1DCF7"/> + <item android:offset="0.066406" android:color="#FFB0DCF6"/> + <item android:offset="0.070312" android:color="#FFB0DCF6"/> + <item android:offset="0.074219" android:color="#FFAFDCF6"/> + <item android:offset="0.078125" android:color="#FFAEDBF6"/> + <item android:offset="0.082031" android:color="#FFAEDBF6"/> + <item android:offset="0.085937" android:color="#FFADDBF6"/> + <item android:offset="0.089844" android:color="#FFACDBF5"/> + <item android:offset="0.09375" android:color="#FFACDBF5"/> + <item android:offset="0.097656" android:color="#FFABDAF5"/> + <item android:offset="0.101562" android:color="#FFAADAF5"/> + <item android:offset="0.105469" android:color="#FFA9DAF5"/> + <item android:offset="0.109375" android:color="#FFA9DAF5"/> + <item android:offset="0.113281" android:color="#FFA8D9F4"/> + <item android:offset="0.117187" android:color="#FFA7D9F4"/> + <item android:offset="0.121094" android:color="#FFA6D9F4"/> + <item android:offset="0.125" android:color="#FFA6D9F4"/> + <item android:offset="0.128906" android:color="#FFA5D8F4"/> + <item android:offset="0.132812" android:color="#FFA4D8F4"/> + <item android:offset="0.136719" android:color="#FFA3D8F3"/> + <item android:offset="0.140625" android:color="#FFA3D8F3"/> + <item android:offset="0.144531" android:color="#FFA2D7F3"/> + <item android:offset="0.148437" android:color="#FFA2D7F3"/> + <item android:offset="0.152344" android:color="#FFA1D7F3"/> + <item android:offset="0.15625" android:color="#FFA0D7F3"/> + <item android:offset="0.160156" android:color="#FF9FD7F3"/> + <item android:offset="0.164062" android:color="#FF9FD7F3"/> + <item android:offset="0.167969" android:color="#FF9ED6F2"/> + <item android:offset="0.171875" android:color="#FF9DD6F2"/> + <item android:offset="0.175781" android:color="#FF9CD6F2"/> + <item android:offset="0.179687" android:color="#FF9CD6F2"/> + <item android:offset="0.183594" android:color="#FF9BD5F2"/> + <item android:offset="0.1875" android:color="#FF9AD5F2"/> + <item android:offset="0.191406" android:color="#FF99D5F1"/> + <item android:offset="0.195312" android:color="#FF99D5F1"/> + <item android:offset="0.199219" android:color="#FF98D4F1"/> + <item android:offset="0.203125" android:color="#FF97D4F1"/> + <item android:offset="0.207031" android:color="#FF96D4F1"/> + <item android:offset="0.210937" android:color="#FF96D4F1"/> + <item android:offset="0.214844" android:color="#FF95D3F0"/> + <item android:offset="0.21875" android:color="#FF95D3F0"/> + <item android:offset="0.222656" android:color="#FF94D3F0"/> + <item android:offset="0.226562" android:color="#FF93D3F0"/> + <item android:offset="0.230469" android:color="#FF92D3F0"/> + <item android:offset="0.234375" android:color="#FF92D2F0"/> + <item android:offset="0.238281" android:color="#FF91D2EF"/> + <item android:offset="0.242187" android:color="#FF90D2EF"/> + <item android:offset="0.246094" android:color="#FF8FD2EF"/> + <item android:offset="0.25" android:color="#FF8FD2EF"/> + <item android:offset="0.253906" android:color="#FF8ED1EF"/> + <item android:offset="0.257812" android:color="#FF8DD1EF"/> + <item android:offset="0.261719" android:color="#FF8CD1EF"/> + <item android:offset="0.265625" android:color="#FF8CD1EF"/> + <item android:offset="0.269531" android:color="#FF8BD0EE"/> + <item android:offset="0.273437" android:color="#FF8AD0EE"/> + <item android:offset="0.277344" android:color="#FF8AD0EE"/> + <item android:offset="0.28125" android:color="#FF89D0EE"/> + <item android:offset="0.285156" android:color="#FF88CFEE"/> + <item android:offset="0.289062" android:color="#FF88CFEE"/> + <item android:offset="0.292969" android:color="#FF87CFED"/> + <item android:offset="0.296875" android:color="#FF86CFED"/> + <item android:offset="0.300781" android:color="#FF85CEED"/> + <item android:offset="0.304687" android:color="#FF85CEED"/> + <item android:offset="0.308594" android:color="#FF84CEED"/> + <item android:offset="0.3125" android:color="#FF83CEED"/> + <item android:offset="0.316406" android:color="#FF82CEEC"/> + <item android:offset="0.320312" android:color="#FF82CEEC"/> + <item android:offset="0.324219" android:color="#FF81CDEC"/> + <item android:offset="0.328125" android:color="#FF80CDEC"/> + <item android:offset="0.332031" android:color="#FF80CCEB"/> + <item android:offset="0.335937" android:color="#FF81CBEA"/> + <item android:offset="0.339844" android:color="#FF81CAE8"/> + <item android:offset="0.34375" android:color="#FF81C9E7"/> + <item android:offset="0.347656" android:color="#FF81C8E6"/> + <item android:offset="0.351562" android:color="#FF82C7E4"/> + <item android:offset="0.355469" android:color="#FF82C6E3"/> + <item android:offset="0.359375" android:color="#FF83C5E2"/> + <item android:offset="0.363281" android:color="#FF83C4E0"/> + <item android:offset="0.367187" android:color="#FF83C3DF"/> + <item android:offset="0.371094" android:color="#FF83C2DD"/> + <item android:offset="0.375" android:color="#FF84C0DC"/> + <item android:offset="0.378906" android:color="#FF84BFDA"/> + <item android:offset="0.382812" android:color="#FF85BED9"/> + <item android:offset="0.386719" android:color="#FF85BDD8"/> + <item android:offset="0.390625" android:color="#FF85BCD7"/> + <item android:offset="0.394531" android:color="#FF85BBD5"/> + <item android:offset="0.398437" android:color="#FF86BAD4"/> + <item android:offset="0.402344" android:color="#FF86B9D2"/> + <item android:offset="0.40625" android:color="#FF87B8D1"/> + <item android:offset="0.410156" android:color="#FF87B7D0"/> + <item android:offset="0.414062" android:color="#FF88B5CE"/> + <item android:offset="0.417969" android:color="#FF88B4CD"/> + <item android:offset="0.421875" android:color="#FF88B3CC"/> + <item android:offset="0.425781" android:color="#FF88B2CA"/> + <item android:offset="0.429687" android:color="#FF89B1C9"/> + <item android:offset="0.433594" android:color="#FF89B0C7"/> + <item android:offset="0.4375" android:color="#FF8AAFC6"/> + <item android:offset="0.441406" android:color="#FF8AAEC4"/> + <item android:offset="0.445312" android:color="#FF8AADC3"/> + <item android:offset="0.449219" android:color="#FF8AACC2"/> + <item android:offset="0.453125" android:color="#FF8BABC1"/> + <item android:offset="0.457031" android:color="#FF8BAABF"/> + <item android:offset="0.460937" android:color="#FF8CA8BE"/> + <item android:offset="0.464844" android:color="#FF8CA7BC"/> + <item android:offset="0.46875" android:color="#FF8CA6BB"/> + <item android:offset="0.472656" android:color="#FF8CA5B9"/> + <item android:offset="0.476562" android:color="#FF8DA4B8"/> + <item android:offset="0.480469" android:color="#FF8DA3B7"/> + <item android:offset="0.484375" android:color="#FF8EA2B6"/> + <item android:offset="0.488281" android:color="#FF8EA1B4"/> + <item android:offset="0.492187" android:color="#FF8FA0B3"/> + <item android:offset="0.496094" android:color="#FF8F9FB1"/> + <item android:offset="0.5" android:color="#FF8F9EB0"/> + <item android:offset="0.503906" android:color="#FF8F9DAF"/> + <item android:offset="0.507812" android:color="#FF909BAD"/> + <item android:offset="0.511719" android:color="#FF909AAC"/> + <item android:offset="0.515625" android:color="#FF9199AB"/> + <item android:offset="0.519531" android:color="#FF9198A9"/> + <item android:offset="0.523437" android:color="#FF9197A8"/> + <item android:offset="0.527344" android:color="#FF9196A6"/> + <item android:offset="0.53125" android:color="#FF9295A5"/> + <item android:offset="0.535156" android:color="#FF9294A3"/> + <item android:offset="0.539063" android:color="#FF9393A2"/> + <item android:offset="0.542969" android:color="#FF9392A1"/> + <item android:offset="0.546875" android:color="#FF9390A0"/> + <item android:offset="0.550781" android:color="#FF938F9E"/> + <item android:offset="0.554687" android:color="#FF948E9D"/> + <item android:offset="0.558594" android:color="#FF948D9B"/> + <item android:offset="0.5625" android:color="#FF958C9A"/> + <item android:offset="0.566406" android:color="#FF958B99"/> + <item android:offset="0.570312" android:color="#FF968A97"/> + <item android:offset="0.574219" android:color="#FF968996"/> + <item android:offset="0.578125" android:color="#FF968894"/> + <item android:offset="0.582031" android:color="#FF968793"/> + <item android:offset="0.585938" android:color="#FF978692"/> + <item android:offset="0.589844" android:color="#FF978490"/> + <item android:offset="0.59375" android:color="#FF98838F"/> + <item android:offset="0.597656" android:color="#FF98828D"/> + <item android:offset="0.601562" android:color="#FF98818C"/> + <item android:offset="0.605469" android:color="#FF98808B"/> + <item android:offset="0.609375" android:color="#FF997F8A"/> + <item android:offset="0.613281" android:color="#FF997E88"/> + <item android:offset="0.617187" android:color="#FF9A7D87"/> + <item android:offset="0.621094" android:color="#FF9A7C85"/> + <item android:offset="0.625" android:color="#FF9A7B84"/> + <item android:offset="0.628906" android:color="#FF9A7A82"/> + <item android:offset="0.632813" android:color="#FF9B7981"/> + <item android:offset="0.636719" android:color="#FF9B7780"/> + <item android:offset="0.640625" android:color="#FF9C767F"/> + <item android:offset="0.644531" android:color="#FF9C757D"/> + <item android:offset="0.648437" android:color="#FF9C747C"/> + <item android:offset="0.652344" android:color="#FF9D737A"/> + <item android:offset="0.65625" android:color="#FF9D7279"/> + <item android:offset="0.660156" android:color="#FF9D7178"/> + <item android:offset="0.664062" android:color="#FF9E7076"/> + <item android:offset="0.667969" android:color="#FF9E6F75"/> + <item android:offset="0.671875" android:color="#FF9F6E74"/> + <item android:offset="0.675781" android:color="#FF9F6C72"/> + <item android:offset="0.679688" android:color="#FF9F6B71"/> + <item android:offset="0.683594" android:color="#FF9F6A6F"/> + <item android:offset="0.6875" android:color="#FFA0696E"/> + <item android:offset="0.691406" android:color="#FFA0686C"/> + <item android:offset="0.695312" android:color="#FFA1676B"/> + <item android:offset="0.699219" android:color="#FFA1666A"/> + <item android:offset="0.703125" android:color="#FFA16569"/> + <item android:offset="0.707031" android:color="#FFA16467"/> + <item android:offset="0.710937" android:color="#FFA26366"/> + <item android:offset="0.714844" android:color="#FFA26264"/> + <item android:offset="0.71875" android:color="#FFA36163"/> + <item android:offset="0.722656" android:color="#FFA35F61"/> + <item android:offset="0.726563" android:color="#FFA45E60"/> + <item android:offset="0.730469" android:color="#FFA45D5F"/> + <item android:offset="0.734375" android:color="#FFA45C5D"/> + <item android:offset="0.738281" android:color="#FFA45B5C"/> + <item android:offset="0.742187" android:color="#FFA55A5B"/> + <item android:offset="0.746094" android:color="#FFA55959"/> + <item android:offset="0.75" android:color="#FFA65858"/> + <item android:offset="0.753906" android:color="#FFA65756"/> + <item android:offset="0.757812" android:color="#FFA65655"/> + <item android:offset="0.761719" android:color="#FFA65454"/> + <item android:offset="0.765625" android:color="#FFA75353"/> + <item android:offset="0.769531" android:color="#FFA75251"/> + <item android:offset="0.773437" android:color="#FFA85150"/> + <item android:offset="0.777344" android:color="#FFA8504E"/> + <item android:offset="0.78125" android:color="#FFA84F4D"/> + <item android:offset="0.785156" android:color="#FFA84E4B"/> + <item android:offset="0.789062" android:color="#FFA94D4A"/> + <item android:offset="0.792969" android:color="#FFA94C49"/> + <item android:offset="0.796875" android:color="#FFAA4B48"/> + <item android:offset="0.800781" android:color="#FFAA4A46"/> + <item android:offset="0.804687" android:color="#FFAB4845"/> + <item android:offset="0.808594" android:color="#FFAB4743"/> + <item android:offset="0.8125" android:color="#FFAB4642"/> + <item android:offset="0.816406" android:color="#FFAB4541"/> + <item android:offset="0.820312" android:color="#FFAC443F"/> + <item android:offset="0.824219" android:color="#FFAC433E"/> + <item android:offset="0.828125" android:color="#FFAD423C"/> + <item android:offset="0.832031" android:color="#FFAD413B"/> + <item android:offset="0.835937" android:color="#FFAD403A"/> + <item android:offset="0.839844" android:color="#FFAD3F38"/> + <item android:offset="0.84375" android:color="#FFAE3E37"/> + <item android:offset="0.847656" android:color="#FFAE3D35"/> + <item android:offset="0.851562" android:color="#FFAF3B34"/> + <item android:offset="0.855469" android:color="#FFAF3A33"/> + <item android:offset="0.859375" android:color="#FFAF3932"/> + <item android:offset="0.863281" android:color="#FFAF3830"/> + <item android:offset="0.867187" android:color="#FFB0372F"/> + <item android:offset="0.871094" android:color="#FFB0362D"/> + <item android:offset="0.875" android:color="#FFB1352C"/> + <item android:offset="0.878906" android:color="#FFB1342A"/> + <item android:offset="0.882812" android:color="#FFB13329"/> + <item android:offset="0.886719" android:color="#FFB23228"/> + <item android:offset="0.890625" android:color="#FFB23026"/> + <item android:offset="0.894531" android:color="#FFB22F25"/> + <item android:offset="0.898437" android:color="#FFB32E24"/> + <item android:offset="0.902344" android:color="#FFB32D22"/> + <item android:offset="0.90625" android:color="#FFB42C21"/> + <item android:offset="0.910156" android:color="#FFB42B1F"/> + <item android:offset="0.914062" android:color="#FFB42A1E"/> + <item android:offset="0.917969" android:color="#FFB4291D"/> + <item android:offset="0.921875" android:color="#FFB5281C"/> + <item android:offset="0.925781" android:color="#FFB5271A"/> + <item android:offset="0.929688" android:color="#FFB62619"/> + <item android:offset="0.933594" android:color="#FFB62517"/> + <item android:offset="0.9375" android:color="#FFB62316"/> + <item android:offset="0.941406" android:color="#FFB62214"/> + <item android:offset="0.945312" android:color="#FFB72113"/> + <item android:offset="0.949219" android:color="#FFB72012"/> + <item android:offset="0.953125" android:color="#FFB81F11"/> + <item android:offset="0.957031" android:color="#FFB81E0F"/> + <item android:offset="0.960938" android:color="#FFB91D0E"/> + <item android:offset="0.964844" android:color="#FFB91C0C"/> + <item android:offset="0.96875" android:color="#FFB91B0B"/> + <item android:offset="0.972656" android:color="#FFB91A09"/> + <item android:offset="0.976562" android:color="#FFBA1908"/> + <item android:offset="0.980469" android:color="#FFBA1807"/> + <item android:offset="0.984375" android:color="#FFBB1605"/> + <item android:offset="0.988281" android:color="#FFBB1504"/> + <item android:offset="0.992188" android:color="#FFBB1403"/> + <item android:offset="0.996094" android:color="#FFBB1301"/> + <item android:offset="1" android:color="#FFBC1200"/> + </gradient> + </aapt:attr> + </path> + <path + android:pathData="M47.107,110.704L92.527,102.393A1.999,2.001 126.099,0 1,94.855 104L95.882,109.586A1.999,2.001 126.099,0 1,94.277 111.913L48.857,120.225A1.999,2.001 126.099,0 1,46.528 118.618L45.501,113.031A1.999,2.001 126.099,0 1,47.107 110.704z" + android:fillColor="#007FAD"/> + <path + android:pathData="M49.101,123.74L94.521,115.429A1.999,2.001 126.099,0 1,96.85 117.036L97.877,122.622A1.999,2.001 126.099,0 1,96.271 124.949L50.851,133.261A1.999,2.001 126.099,0 1,48.522 131.654L47.495,126.067A1.999,2.001 126.099,0 1,49.101 123.74z" + android:fillColor="#007FAD"/> + <path + android:pathData="M39.442,60.918l42.77,-8.114l0.768,4.036l-42.77,8.114z" + android:fillColor="#C4C4C4"/> + <path + android:pathData="M40.849,68.312l37.019,-7.023l0.768,4.036l-37.019,7.023z" + android:fillColor="#C4C4C4"/> + <path + android:pathData="M42.256,75.706l32.98,-6.256l0.768,4.036l-32.98,6.256z" + android:fillColor="#C4C4C4"/> + </group> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_incidence.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_incidence.xml new file mode 100644 index 0000000000000000000000000000000000000000..bb0ff2f3909c1edb9e1a28ab0c671c04ae7a010e --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_incidence.xml @@ -0,0 +1,357 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="106dp" + android:height="136dp" + android:viewportWidth="106" + android:viewportHeight="136"> + <path + android:fillColor="#2D2D2F" + android:pathData="M43.352,66.678C43.289,68.786 39.528,68.243 40.418,65.955C41.091,63.094 40.299,63.96 42.853,63.847C42.975,64.088 43.372,65.99 43.352,66.678Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M32.832,65.373C33.528,64.967 35.792,64.099 35.428,63.161L37.389,62.855C37.407,63.216 38.405,65.822 37.2,66.063C36.225,66.261 30.068,67.943 32.832,65.373Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M44.2,33.641C44.2,33.641 44.419,36.12 44.457,36.592C44.815,41.012 43.62,60.1 43.3,62.95C43.278,63.146 43.172,63.747 42.834,63.877C42.142,64.142 41.071,63.877 41.071,63.877C40.878,63.877 40.718,63.72 40.709,63.522C40.565,60.324 39.117,41.834 38.898,40.126C38.047,43.307 37.895,59.275 37.541,63.299C37.522,63.526 37.337,63.699 37.116,63.699C37.116,63.699 36.225,63.875 35.801,63.699C35.352,63.512 35.299,62.935 35.28,62.76C34.36,54.583 32.787,41.169 33.71,32.96L36.957,34.405L44.2,33.641Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M41.633,11.802C41.633,11.802 36.623,11.451 36.62,11.457L36.525,11.78C36.232,10.801 35.091,8.07 36.561,7.206C37.474,4.984 42.561,5.99 42.399,8.944C42.457,9.291 41.633,11.802 41.633,11.802Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M31.64,31.718L30.971,31.771C30.472,32.887 30.359,34.687 31.169,35.771C31.983,36.533 32.857,35.283 32.899,34.578C33.027,33.468 32.652,32.498 32.337,31.657L31.64,31.718Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M31.132,22.967C31.272,22.173 31.45,21.401 31.67,20.659C32.148,18.813 32.341,17.577 33.782,16.592C34.378,16.184 35.158,15.899 36.225,15.532C36.611,15.4 36.881,15.159 36.962,14.829C37.063,14.426 40.071,14.597 40.819,14.678C40.928,14.69 40.929,14.589 40.975,14.693C41.113,15.002 41.37,15.4 42.014,15.665C42.883,16.022 44.99,16.727 45.789,18.177C47.03,20.433 49.088,29.44 47.681,30.421C47.049,30.862 44.717,31.228 44.339,31.294C44.336,32.455 44.329,34.075 44.226,34.319C43.423,36.22 36.528,36.641 33.645,34.49C33.333,34.257 33.73,26.225 34.237,22.643C34.182,22.882 34.123,23.142 34.062,23.416C33.748,24.837 33.524,26.526 33.267,28.247C33.014,29.943 32.881,31.956 32.659,32.169C31.936,32.859 31.43,32.822 30.935,32.686C30.648,32.606 30.41,32.359 30.401,32.053C30.309,28.645 30.61,25.924 31.132,22.967Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M38.815,26.217H40.584C40.815,26.217 41.002,26.409 41.002,26.647V31.086C41.002,31.323 40.816,31.516 40.584,31.516H38.847C38.617,31.516 38.429,31.324 38.429,31.086V26.653C38.429,26.413 38.614,26.218 38.843,26.218" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M39.002,15.944C38.217,15.784 37.348,15.288 36.98,14.79C37.102,14.324 37.297,13.63 37.283,13.537C37.481,13.756 40.521,13.761 40.722,13.537C40.708,13.63 40.903,14.324 41.024,14.79C40.654,15.288 39.788,15.782 39.002,15.944Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M41.644,11.912V11.854C42.039,11.57 42.287,11.058 42.275,10.666C42.276,10.458 42.195,9.964 41.97,10.181C41.911,10.446 41.716,11.369 41.523,11.516C41.686,5.741 38.067,11.079 37.045,8.642C36.285,8.904 36.943,11.143 36.694,11.508C36.488,11.356 36.353,10.454 36.3,10.181C36.254,10.145 36.211,10.116 36.175,10.131C35.757,10.592 36.099,11.474 36.628,11.856C36.589,13.27 38.169,14.908 39.136,14.694C40.098,14.906 41.66,13.3 41.644,11.886" /> + <path + android:fillColor="#E4B489" + android:pathData="M41.499,11.31C41.5,11.255 41.502,11.205 41.502,11.153C41.503,11.21 41.503,11.269 41.503,11.328C41.502,11.322 41.5,11.316 41.499,11.31Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M42.802,30.058L42.808,29.42C41.818,28.838 40.164,28.577 39.095,29.282C38.322,30.017 39.406,30.977 40.055,31.077C41.071,31.294 41.998,31.008 42.803,30.768" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M20.766,20.17C20.766,20.17 25.102,21.906 29.358,19.665L28.435,16.18L22.033,16.251L20.766,20.17Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M23.646,18.726L23.531,21.948H26.507L26.068,18.726H23.646Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M27.508,16.597C27.254,21.552 22.301,20.665 22.422,15.688C22.675,10.734 27.63,11.62 27.508,16.597Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M21.135,35.607C19.048,43.938 21.051,55.908 21.697,65.516L23.212,65.565C23.212,65.565 24.081,49.941 24.449,46.388C24.561,45.308 24.927,43.002 25.113,41.998C25.128,41.922 25.289,41.59 25.309,41.977C25.431,44.428 26.536,47.123 26.782,49.818C27.355,56.117 27.229,60.501 27.478,65.7C27.478,65.7 29.234,66.736 29.237,66.725C29.604,65.274 30.587,53.325 30.587,51.533C30.587,51.166 30.71,43.08 29.114,35.729L21.135,35.607Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M31.569,36.354C30.647,40.626 36.835,41.452 32.354,35.471L31.569,36.354Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M32.817,35.264C32.587,33.646 32.184,31.174 31.815,29.481C31.201,26.663 30.465,22.375 26.413,21.273C25.323,21.998 24.34,21.403 23.58,21.265C21.105,20.817 18.898,24.92 18.311,27.031C17.697,29.236 16.645,32.523 17.119,33.279C18.04,34.749 18.841,33.963 21.048,32.421C21.153,32.348 21.26,32.271 21.368,32.194C21.333,33.675 21.265,35.008 21.136,35.607C20.839,36.992 29.484,36.954 29.238,35.974C28.718,33.899 28.501,30.094 28.501,27.153C28.501,26.908 29.114,28.154 29.361,28.991C29.975,31.074 30.501,34.634 31.316,36.403C31.571,36.954 31.931,36.725 32.528,36.519C33.039,36.337 32.846,35.476 32.817,35.264ZM20.03,30.584C20.03,30.584 20.153,30.461 20.644,28.746C20.932,27.739 21.38,26.173 21.38,26.173C21.38,26.173 21.41,28.031 21.396,30.125C20.468,30.406 20.03,30.584 20.03,30.584Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M23.258,64.844L21.627,64.642C21.58,65.061 21.013,65.745 21.326,67.371C21.351,67.501 21.381,68.737 21.423,68.87C21.461,68.994 21.995,69.174 21.995,69.052C21.995,68.928 21.843,67.401 22.118,67.949C22.241,68.194 22.241,69.052 22.854,69.542C23.556,70.102 25.764,70.282 24.941,69.297C23.714,67.826 23.343,65.49 23.247,64.976" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M27.447,64.859C27.447,64.859 27.434,65.718 27.274,66.359C27.151,66.849 27.028,68.319 27.519,69.544H28.256C28.256,69.544 28.01,68.197 28.133,67.461C28.166,67.265 28.256,68.319 28.993,69.177C29.331,69.571 29.636,69.625 29.779,69.641C30.043,69.67 31.61,69.667 31.693,69.667C31.939,69.667 32.184,69.177 31.199,68.742C30.935,68.625 30.606,68.402 30.343,68.074C29.896,67.517 29.709,66.559 29.464,65.211L27.447,64.859Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M25.383,25.725L27.059,25.758C27.279,25.763 27.438,25.944 27.415,26.162L26.979,30.206C26.956,30.426 26.758,30.6 26.538,30.595L24.863,30.562C24.643,30.557 24.483,30.375 24.507,30.157L24.943,26.113C24.966,25.894 25.162,25.72 25.383,25.725Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M23.094,30.226L23.445,30.742C25.678,30.726 28.031,27.663 25.085,28.003C24.91,28.024 24.739,28.079 24.585,28.166C23.859,28.574 23.308,29.214 22.778,29.689" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M25.922,13.554C25.186,13.309 23.712,13.432 23.207,17.954C23.137,18.583 23.59,19.19 23.431,19.535C22.96,20.562 22.34,21.035 21.419,20.769C18.925,20.047 20.901,18.956 21.023,16.498C21.257,11.717 24.605,10.99 26.008,11.759C26.008,11.759 28.665,10.975 29.024,16.583C29.024,16.583 29.21,19.479 29.612,19.992C30.014,20.506 28.868,20.901 27.924,20.531C26.98,20.161 27.274,18.063 27.274,18.063C27.274,18.063 28.064,14.267 25.922,13.554Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M13.352,68.678C13.289,70.786 9.528,70.243 10.418,67.955C11.091,65.094 10.299,65.96 12.853,65.847C12.975,66.088 13.371,67.99 13.352,68.678Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M2.832,67.373C3.528,66.967 5.792,66.099 5.428,65.161L7.389,64.855C7.407,65.216 8.405,67.822 7.2,68.063C6.225,68.261 0.068,69.943 2.832,67.373Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M14.2,35.641C14.2,35.641 14.418,38.12 14.457,38.592C14.815,43.012 13.62,62.1 13.3,64.95C13.278,65.146 13.172,65.747 12.834,65.877C12.142,66.142 11.07,65.877 11.07,65.877C10.877,65.877 10.718,65.72 10.709,65.522C10.565,62.324 9.117,43.834 8.898,42.126C8.047,45.307 7.895,61.275 7.541,65.299C7.522,65.526 7.337,65.699 7.115,65.699C7.115,65.699 6.225,65.875 5.801,65.699C5.352,65.512 5.299,64.935 5.28,64.76C4.36,56.583 2.787,43.169 3.709,34.96L6.957,36.405L14.2,35.641Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M11.632,13.802C11.632,13.802 6.623,13.451 6.62,13.457L6.525,13.78C6.232,12.801 5.091,10.07 6.561,9.206C7.474,6.984 12.561,7.99 12.399,10.944C12.457,11.291 11.632,13.802 11.632,13.802Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M1.64,33.717L0.971,33.771C0.472,34.887 0.359,36.687 1.169,37.771C1.983,38.533 2.857,37.283 2.899,36.578C3.026,35.468 2.652,34.498 2.337,33.657L1.64,33.717Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M1.132,24.967C1.272,24.173 1.449,23.401 1.669,22.659C2.148,20.813 2.341,19.577 3.781,18.592C4.378,18.184 5.158,17.899 6.225,17.532C6.611,17.4 6.881,17.159 6.962,16.829C7.063,16.426 10.071,16.597 10.818,16.678C10.928,16.69 10.929,16.589 10.975,16.692C11.113,17.002 11.37,17.4 12.013,17.665C12.883,18.022 14.99,18.727 15.788,20.177C17.029,22.433 19.087,31.44 17.68,32.421C17.049,32.862 14.717,33.228 14.339,33.294C14.336,34.455 14.328,36.075 14.226,36.319C13.423,38.22 6.527,38.641 3.645,36.49C3.332,36.257 3.73,28.225 4.237,24.643C4.181,24.882 4.122,25.142 4.062,25.416C3.748,26.837 3.524,28.526 3.267,30.247C3.013,31.943 2.881,33.956 2.658,34.169C1.936,34.859 1.43,34.822 0.935,34.686C0.648,34.606 0.41,34.359 0.401,34.053C0.309,30.645 0.61,27.924 1.132,24.967Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M8.814,28.217H10.584C10.814,28.217 11.002,28.409 11.002,28.647V33.086C11.002,33.323 10.816,33.516 10.584,33.516H8.847C8.616,33.516 8.429,33.324 8.429,33.086V28.653C8.429,28.413 8.614,28.218 8.843,28.218" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M9.002,17.944C8.216,17.784 7.348,17.288 6.98,16.79C7.101,16.324 7.297,15.63 7.283,15.537C7.481,15.756 10.521,15.761 10.722,15.537C10.708,15.63 10.903,16.324 11.024,16.79C10.654,17.288 9.788,17.782 9.002,17.944Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M11.644,13.912V13.854C12.039,13.57 12.287,13.058 12.274,12.666C12.276,12.458 12.195,11.964 11.969,12.181C11.91,12.446 11.716,13.369 11.523,13.516C11.685,7.741 8.067,13.079 7.045,10.642C6.284,10.904 6.943,13.143 6.693,13.508C6.488,13.356 6.353,12.454 6.3,12.181C6.253,12.145 6.211,12.116 6.175,12.131C5.757,12.592 6.099,13.474 6.628,13.856C6.589,15.27 8.169,16.908 9.136,16.694C10.098,16.906 11.66,15.3 11.644,13.886" /> + <path + android:fillColor="#E4B489" + android:pathData="M11.499,13.31C11.5,13.255 11.502,13.205 11.502,13.153C11.503,13.21 11.503,13.269 11.503,13.328C11.502,13.322 11.5,13.316 11.499,13.31Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M12.802,32.058L12.808,31.42C11.818,30.838 10.164,30.577 9.095,31.282C8.322,32.017 9.406,32.977 10.054,33.077C11.07,33.294 11.998,33.008 12.803,32.768" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M65.872,20.458L65.56,23.689L64.925,24.21L64.291,24.601L65.179,26.556L70.13,26.686L69.495,24.601L69.622,23.559L69.235,23.384C68.924,23.243 68.705,22.948 68.66,22.601L68.377,20.457H65.872V20.458Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M69.866,18.194C69.603,23.465 64.481,22.521 64.607,17.228C64.868,11.957 69.991,12.9 69.866,18.194Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M63.275,38.415C61.117,47.277 63.189,60.011 63.857,70.23L65.423,70.282C65.423,70.282 66.322,53.663 66.702,49.883C66.818,48.735 67.196,46.281 67.389,45.214C67.404,45.133 67.571,44.78 67.591,45.192C67.718,47.798 68.86,50.665 69.114,53.532C69.707,60.232 69.576,64.896 69.834,70.426C69.834,70.426 71.65,71.528 71.653,71.517C72.032,69.974 73.049,57.262 73.049,55.357C73.049,54.966 73.544,46.825 71.526,38.546L63.275,38.415Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M73.63,39.011C72.438,43.495 78.781,44.728 74.491,38.118L73.63,39.011Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M59.123,35.939C60.075,37.503 60.904,36.666 63.185,35.027C63.294,34.949 63.403,34.867 63.516,34.784C63.481,36.359 63.41,37.778 63.276,38.415C62.969,39.889 71.908,39.849 71.654,38.806C71.116,36.598 70.892,32.551 70.892,29.423C70.892,29.162 71.316,30.476 71.527,31.378C72.063,33.662 72.452,36.989 73.295,38.871C73.558,39.458 73.931,39.214 74.548,38.995C75.076,38.801 74.863,37.886 74.846,37.658C74.727,36.008 74.446,33.699 74.064,31.898C73.478,29.126 73.036,25.016 69.62,23.486C69.565,23.461 69.5,23.504 69.501,23.568C69.534,24.737 68.327,26.424 67.718,26.424C66.575,26.424 64.671,24.73 64.407,23.362C61.69,24.176 60.882,27.346 60.356,29.291C59.721,31.639 58.633,35.135 59.123,35.939Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M65.467,69.501L63.783,69.302C63.734,69.748 63.148,70.475 63.472,72.205C63.497,72.343 63.529,73.658 63.572,73.8C63.611,73.932 64.163,74.123 64.163,73.993C64.163,73.861 64.006,72.237 64.29,72.82C64.417,73.08 64.417,73.993 65.052,74.514C65.777,75.11 68.06,75.301 67.21,74.253C65.94,72.689 65.561,70.21 65.462,69.662" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M69.815,69.585C69.815,69.585 69.788,70.445 69.622,71.126C69.495,71.647 69.368,73.211 69.876,74.514H70.637C70.637,74.514 70.383,73.08 70.51,72.298C70.545,72.09 70.637,73.211 71.399,74.123C71.749,74.543 72.064,74.6 72.212,74.617C72.485,74.648 74.105,74.644 74.191,74.644C74.445,74.644 74.699,74.123 73.68,73.66C73.407,73.536 73.067,73.299 72.795,72.95C72.333,72.357 72.121,71.354 71.867,69.92L69.815,69.585Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M67.668,27.903L69.401,27.938C69.628,27.944 69.793,28.137 69.769,28.368L69.318,32.67C69.294,32.904 69.09,33.089 68.863,33.083L67.13,33.048C66.903,33.043 66.738,32.85 66.762,32.618L67.213,28.316C67.237,28.083 67.44,27.898 67.668,27.903Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M65.301,32.691L65.664,33.24C68.036,33.223 70.537,29.788 67.101,30.364C66.217,30.781 65.583,31.558 64.975,32.119" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M70.783,24.183C70.604,24.043 70.457,23.942 70.256,23.82C70.024,23.68 69.86,23.603 69.604,23.492C69.584,23.483 69.537,23.492 69.523,23.519C69.307,23.465 69.136,23.366 68.957,23.195C68.58,22.835 68.339,22.197 68.465,21.545C68.534,21.193 68.666,21.047 68.738,20.957C70.369,18.939 69.296,15.726 69.296,15.726C69.296,15.726 68.379,17.71 64.922,19.068C64.922,19.068 65.065,19.99 65.573,20.642C65.609,20.69 65.99,21.043 66.004,21.597C66.017,22.141 66.018,22.691 65.656,23.523C65.216,24.54 64.095,24.785 63.245,24.436C62.61,24.175 62.356,23.523 62.327,22.881C62.327,22.881 64.057,22.985 63.65,21.002C63.345,19.511 62.982,16.254 64.415,14.371C64.435,14.345 64.456,14.319 64.476,14.293C64.49,14.273 64.506,14.254 64.523,14.235C64.552,14.2 64.583,14.165 64.613,14.131C64.642,14.1 64.671,14.069 64.701,14.037C65.15,13.569 65.741,13.218 66.515,13.045C66.515,13.045 69.464,12.628 69.972,14.403C69.972,14.403 70.786,14.822 70.888,16.388C70.99,17.955 70.735,18.528 70.684,19.73C70.623,21.191 72.5,22 71.89,22.881C71.39,23.5 72.164,23.762 70.783,24.183Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M95.872,17.458L95.56,20.689L94.925,21.21L94.29,21.601L95.179,23.556L100.129,23.686L99.495,21.601L99.622,20.559L99.234,20.384C98.923,20.243 98.705,19.948 98.659,19.601L98.376,17.457H95.872V17.458Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M99.865,15.194C99.603,20.465 94.481,19.521 94.606,14.227C94.868,8.957 99.991,9.9 99.865,15.194Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M93.275,35.415C91.117,44.277 93.189,57.011 93.856,67.23L95.423,67.282C95.423,67.282 96.321,50.663 96.702,46.883C96.818,45.735 97.196,43.281 97.389,42.214C97.404,42.133 97.57,41.78 97.591,42.192C97.718,44.798 98.86,47.665 99.114,50.532C99.707,57.232 99.576,61.896 99.834,67.426C99.834,67.426 101.65,68.528 101.652,68.517C102.032,66.974 103.049,54.262 103.049,52.357C103.049,51.966 103.544,43.825 101.526,35.546L93.275,35.415Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M103.63,36.011C102.438,40.495 108.781,41.728 104.491,35.118L103.63,36.011Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M90.5,31.5C92.344,34.53 89.5,35 93.276,35.415C92.969,36.889 101.907,36.849 101.654,35.806C101.115,33.598 100.892,29.551 100.892,26.423C100.892,26.162 101.316,27.476 101.527,28.378C102.062,30.662 102.452,33.989 103.295,35.871C103.558,36.458 103.931,36.214 104.548,35.995C105.076,35.801 104.862,34.886 104.846,34.658C104.727,33.008 104.446,30.699 104.064,28.898C103.478,26.126 103.036,22.016 99.62,20.486C99.564,20.461 99.499,20.504 99.501,20.568C99.534,21.737 98.327,23.424 97.717,23.424C96.575,23.424 94.671,21.73 94.407,20.362C91.689,21.176 91.5,23.424 91.5,26.5C90.865,28.847 90.01,30.696 90.5,31.5Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M95.467,66.501L93.782,66.302C93.734,66.748 93.148,67.475 93.471,69.205C93.497,69.343 93.529,70.658 93.572,70.8C93.611,70.932 94.163,71.123 94.163,70.993C94.163,70.861 94.006,69.237 94.29,69.82C94.417,70.08 94.417,70.993 95.052,71.514C95.776,72.11 98.06,72.301 97.21,71.253C95.94,69.689 95.561,67.21 95.462,66.662" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M99.814,66.585C99.814,66.585 99.788,67.445 99.621,68.126C99.494,68.647 99.368,70.211 99.875,71.514H100.637C100.637,71.514 100.383,70.08 100.51,69.298C100.544,69.09 100.637,70.211 101.398,71.123C101.749,71.543 102.064,71.6 102.212,71.617C102.485,71.648 104.105,71.644 104.191,71.644C104.445,71.644 104.699,71.123 103.679,70.66C103.407,70.536 103.066,70.299 102.795,69.95C102.333,69.357 102.121,68.354 101.867,66.92L99.814,66.585Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M97.668,24.903L99.401,24.938C99.628,24.944 99.793,25.137 99.769,25.368L99.318,29.67C99.294,29.904 99.09,30.089 98.862,30.083L97.13,30.048C96.902,30.043 96.738,29.85 96.762,29.618L97.212,25.316C97.236,25.083 97.439,24.898 97.668,24.903Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M95.301,29.691L95.664,30.24C98.036,30.223 100.537,26.788 97.101,27.364C96.217,27.781 95.582,28.558 94.974,29.119" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M100.783,21.183C100.604,21.043 100.457,20.942 100.256,20.82C100.024,20.68 99.86,20.603 99.604,20.492C99.583,20.483 99.536,20.492 99.522,20.519C99.307,20.465 99.135,20.366 98.956,20.195C98.579,19.835 98.338,19.197 98.465,18.545C98.534,18.193 98.666,18.047 98.738,17.957C100.369,15.939 99.295,12.726 99.295,12.726C99.295,12.726 98.379,14.71 94.921,16.068C94.921,16.068 95.065,16.99 95.572,17.642C95.609,17.69 95.99,18.043 96.004,18.597C96.017,19.141 96.018,19.691 95.656,20.523C95.216,21.54 94.095,21.785 93.244,21.436C92.61,21.175 92.356,20.523 92.327,19.881C92.327,19.881 94.057,19.985 93.649,18.002C93.345,16.511 92.982,13.254 94.415,11.371C94.435,11.345 94.455,11.319 94.476,11.293C94.49,11.273 94.506,11.254 94.523,11.235C94.552,11.2 94.582,11.165 94.613,11.131C94.642,11.1 94.671,11.069 94.7,11.037C95.15,10.569 95.741,10.218 96.514,10.045C96.514,10.045 99.464,9.629 99.972,11.403C99.972,11.403 100.785,11.822 100.888,13.388C100.99,14.955 100.735,15.528 100.684,16.73C100.623,18.191 100.582,19.654 102.108,19.967C102.111,19.964 102.164,20.762 100.783,21.183Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M85.954,69.261C85.888,71.409 81.936,70.855 82.871,68.524C83.578,65.61 82.745,66.492 85.43,66.377C85.558,66.623 85.975,68.56 85.954,69.261Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M74.9,67.932C75.631,67.518 78.01,66.634 77.627,65.679L79.688,65.367C79.707,65.734 80.756,68.389 79.49,68.635C78.465,68.836 71.995,70.549 74.9,67.932Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M86.845,35.607C86.845,35.607 87.075,38.133 87.116,38.613C87.491,43.116 86.236,62.561 85.899,65.463C85.876,65.662 85.765,66.275 85.41,66.407C84.683,66.677 83.557,66.407 83.557,66.407C83.354,66.407 83.186,66.248 83.177,66.046C83.025,62.789 81.504,43.954 81.274,42.213C80.379,45.454 80.22,61.72 79.848,65.819C79.828,66.05 79.633,66.226 79.4,66.226C79.4,66.226 78.465,66.406 78.019,66.226C77.548,66.036 77.492,65.448 77.472,65.27C76.505,56.94 74.852,43.276 75.822,34.914L79.234,36.386L86.845,35.607Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M84.147,13.362C84.147,13.362 78.883,13.004 78.88,13.009L78.78,13.339C78.472,12.342 77.273,9.56 78.818,8.68C79.778,6.416 85.123,7.441 84.953,10.45C85.014,10.804 84.147,13.362 84.147,13.362Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M73.647,33.648L72.944,33.702C72.42,34.84 72.301,36.673 73.152,37.777C74.008,38.554 74.925,37.281 74.97,36.563C75.104,35.432 74.711,34.443 74.379,33.586L73.647,33.648Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M73.113,24.734C73.26,23.926 73.447,23.139 73.678,22.384C74.181,20.503 74.383,19.244 75.897,18.24C76.524,17.825 77.343,17.535 78.465,17.161C78.871,17.026 79.155,16.781 79.24,16.444C79.345,16.034 82.507,16.208 83.292,16.291C83.407,16.303 83.408,16.2 83.457,16.306C83.601,16.621 83.872,17.026 84.547,17.296C85.461,17.66 87.675,18.378 88.514,19.855C89.819,22.153 91.981,31.328 90.503,32.327C89.839,32.777 87.388,33.149 86.991,33.217C86.988,34.4 86.98,36.05 86.872,36.298C85.234,37.382 80.017,37.657 75.754,37.021C73.976,37.021 74.717,36.298 74.717,34.108C74.717,34.911 73.426,34.773 72.906,34.635C72.605,34.554 72.355,34.301 72.345,33.99C72.248,30.518 72.564,27.747 73.113,24.734ZM87.086,30.279C87.521,30.113 87.712,30.192 87.926,30.113C87.957,29.103 86.967,24.947 86.845,24.582C86.845,24.599 87.009,27.337 87.086,30.279Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M81.186,28.045H83.046C83.288,28.045 83.485,28.241 83.485,28.484V33.005C83.485,33.246 83.289,33.444 83.046,33.444H81.22C80.978,33.444 80.78,33.248 80.78,33.005V28.489C80.78,28.245 80.975,28.047 81.216,28.047" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M81.383,17.581C80.557,17.418 79.645,16.913 79.258,16.406C79.385,15.931 79.591,15.224 79.576,15.13C79.784,15.352 82.979,15.358 83.19,15.13C83.175,15.224 83.381,15.931 83.508,16.406C83.119,16.913 82.209,17.416 81.383,17.581Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M84.16,13.474V13.414C84.574,13.126 84.835,12.604 84.822,12.204C84.823,11.992 84.738,11.489 84.502,11.71C84.439,11.98 84.235,12.921 84.033,13.07C84.203,7.188 80.401,12.625 79.326,10.143C78.527,10.41 79.219,12.69 78.957,13.062C78.741,12.907 78.599,11.988 78.544,11.71C78.495,11.674 78.45,11.644 78.412,11.659C77.973,12.129 78.333,13.027 78.888,13.417C78.848,14.857 80.507,16.526 81.524,16.307C82.535,16.523 84.176,14.888 84.16,13.447" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M84.007,12.86C84.008,12.804 84.009,12.753 84.009,12.701C84.011,12.759 84.011,12.818 84.011,12.879C84.009,12.872 84.008,12.866 84.007,12.86Z" /> + <path + android:fillColor="#2D2D2F" + android:pathData="M85.376,31.958L85.383,31.308C84.342,30.715 82.604,30.449 81.481,31.167C80.668,31.916 81.808,32.894 82.489,32.995C83.557,33.217 84.531,32.925 85.377,32.681" /> + <path + android:fillColor="#3F3F43" + android:pathData="M71.09,33.964L70.932,38.365H75.002L74.402,33.964H71.09Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M76.372,31.057C76.024,37.824 69.25,36.613 69.416,29.815C69.762,23.048 76.538,24.258 76.372,31.057Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M67.655,57.022C64.801,68.401 67.541,84.752 68.424,97.875L70.496,97.942C70.496,97.942 71.684,76.601 72.188,71.748C72.341,70.273 72.841,66.453 73.096,65.082C73.116,64.979 73.336,64.525 73.363,65.054C73.531,68.401 75.042,72.752 75.378,76.433C76.162,85.036 75.989,91.026 76.33,98.126C76.33,98.126 78.732,99.542 78.736,99.527C79.238,97.545 80.583,81.223 80.583,78.776C80.583,78.274 80.751,67.229 78.568,57.189L67.655,57.022Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M81.926,58.043C80.665,63.878 89.127,65.006 82.999,56.836L81.926,58.043Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M83.632,56.553C83.318,54.344 82.767,50.967 82.261,48.655C81.422,44.806 80.415,38.949 74.874,37.443C73.944,38.338 71.748,37.751 70.999,37.443C70.986,37.438 70.981,37.443 70.966,37.443C65.85,37.493 64.597,42.423 63.793,45.308C62.954,48.32 61.515,52.81 62.163,53.842C63.422,55.85 64.519,54.776 67.535,52.671C67.68,52.57 67.826,52.465 67.974,52.36C67.927,54.381 67.833,56.203 67.656,57.022C67.25,58.914 79.073,58.862 78.737,57.524C78.026,54.689 77.73,49.491 77.73,45.475C77.73,45.14 78.568,46.842 78.905,47.985C79.745,50.83 80.465,55.693 81.58,58.11C81.927,58.862 82.421,58.55 83.237,58.269C83.935,58.019 83.672,56.843 83.632,56.553ZM66.144,50.161C66.144,50.161 66.312,49.993 66.983,47.651C67.378,46.275 67.991,44.136 67.991,44.136C67.991,44.136 68.031,46.673 68.012,49.535C66.743,49.918 66.144,50.161 66.144,50.161Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M70.489,97.098L68.326,96.682C68.262,97.254 67.487,98.188 67.915,100.408C67.949,100.586 67.991,102.274 68.048,102.457C68.1,102.626 68.83,102.872 68.83,102.704C68.83,102.535 68.622,100.45 68.998,101.198C69.166,101.533 69.166,102.704 70.005,103.374C70.964,104.138 73.984,104.384 72.859,103.039C71.18,101.031 70.689,97.759 70.558,97.057" /> + <path + android:fillColor="#3F3F43" + android:pathData="M68.469,22.33C68.469,22.33 69.206,20.688 70.533,21.073C70.533,21.073 71.016,18.496 74.303,19.088C77.591,19.681 77.535,21.925 77.535,21.925C77.535,21.925 81.043,21.93 80.113,26.055C80.113,26.055 80.944,27.923 79.617,28.873C79.617,28.873 80.082,30.925 78.032,31.157C78.032,31.157 77.871,31.76 77.361,31.656C77.361,31.656 76.456,32.811 76.127,32.031C75.93,31.564 75.974,30.64 76.041,29.974C76.1,29.387 75.925,28.803 75.551,28.346C75.094,27.79 74.409,27.156 73.625,27.103C72.205,27.005 70.249,27.998 69.98,29.025C69.779,29.793 70.35,32.267 69.131,32.273C68.353,32.278 67.979,31.144 67.979,31.144C67.979,31.144 65.719,30.761 66.354,28.975C66.354,28.975 65.214,27.671 66.165,26.12C66.165,26.122 65.076,22.383 68.469,22.33Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M76.298,97.04C76.298,97.04 76.269,98.151 76.051,99.025C75.883,99.694 75.715,101.702 76.387,103.376H77.394C77.394,103.376 77.059,101.535 77.226,100.531C77.272,100.263 77.394,101.702 78.402,102.874C78.865,103.412 79.281,103.486 79.478,103.508C79.839,103.548 81.981,103.543 82.095,103.543C82.431,103.543 82.767,102.874 81.419,102.28C81.058,102.121 80.608,101.816 80.248,101.368C79.637,100.606 79.387,99.316 79.051,97.475L76.298,97.04Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M73.465,43.524L75.757,43.569C76.058,43.576 76.276,43.823 76.244,44.121L75.648,49.645C75.616,49.945 75.346,50.182 75.045,50.176L72.754,50.13C72.453,50.124 72.235,49.876 72.267,49.578L72.863,44.054C72.895,43.755 73.163,43.517 73.465,43.524Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M70.334,49.672L70.814,50.377C73.951,50.355 77.26,45.944 72.715,46.683C71.547,47.219 70.707,48.216 69.903,48.937" /> + <path + android:fillColor="#3F3F43" + android:pathData="M15.298,33.864L14.875,38.137L14.017,38.826L13.158,39.344L14.36,41.929L21.058,42.101L20.199,39.344L20.371,37.965L19.847,37.734C19.426,37.548 19.131,37.156 19.069,36.698L18.686,33.862H15.298V33.864Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M20.701,30.87C20.345,37.84 13.416,36.592 13.586,29.591C13.939,22.621 20.871,23.867 20.701,30.87Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M11.784,57.613C8.865,69.334 11.667,86.175 12.571,99.692L14.69,99.761C14.69,99.761 15.906,77.78 16.421,72.781C16.577,71.263 17.089,68.017 17.35,66.605C17.371,66.499 17.596,66.032 17.623,66.576C17.795,70.023 19.34,73.815 19.684,77.607C20.486,86.468 20.309,92.637 20.658,99.95C20.658,99.95 23.115,101.408 23.118,101.393C23.632,99.352 25.007,82.54 25.007,80.02C25.007,79.503 25.677,68.736 22.947,57.786L11.784,57.613Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M25.794,58.401C24.181,64.332 32.763,65.963 26.958,57.221L25.794,58.401Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M6.167,54.339C7.455,56.407 8.576,55.3 11.662,53.132C11.81,53.029 11.957,52.92 12.11,52.812C12.062,54.894 11.966,56.771 11.786,57.613C11.37,59.563 23.463,59.509 23.12,58.131C22.392,55.211 22.09,49.857 22.09,45.721C22.09,45.376 22.663,47.113 22.948,48.306C23.673,51.327 24.2,55.728 25.34,58.217C25.696,58.992 26.201,58.67 27.035,58.381C27.75,58.124 27.461,56.914 27.439,56.612C27.278,54.43 26.898,51.376 26.381,48.994C25.588,45.328 24.99,39.891 20.369,37.868C20.293,37.835 20.206,37.892 20.207,37.977C20.252,39.523 18.619,41.755 17.795,41.755C16.249,41.755 13.673,39.514 13.316,37.704C9.639,38.782 8.547,42.973 7.834,45.547C6.975,48.651 5.504,53.275 6.167,54.339ZM10.359,50.295C10.479,49.997 10.702,49.35 11.099,47.961L11.915,45.098C11.95,44.971 12.138,44.995 12.139,45.128C12.15,46.12 12.165,47.937 12.151,49.902C11.343,50.147 10.798,50.336 10.505,50.445C10.41,50.481 10.321,50.388 10.359,50.295Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M14.75,98.727L12.471,98.465C12.405,99.054 11.612,100.016 12.05,102.303C12.084,102.486 12.127,104.225 12.186,104.413C12.239,104.587 12.986,104.84 12.986,104.668C12.986,104.494 12.773,102.346 13.158,103.116C13.329,103.461 13.329,104.668 14.188,105.357C15.169,106.145 18.258,106.398 17.107,105.012C15.39,102.944 14.877,99.664 14.743,98.94" /> + <path + android:fillColor="#3F3F43" + android:pathData="M20.632,98.838C20.632,98.838 20.596,99.976 20.371,100.876C20.199,101.565 20.027,103.633 20.714,105.357H21.744C21.744,105.357 21.401,103.461 21.573,102.427C21.619,102.151 21.744,103.633 22.775,104.84C23.249,105.395 23.675,105.471 23.876,105.493C24.245,105.535 26.436,105.529 26.553,105.529C26.896,105.529 27.24,104.84 25.861,104.228C25.492,104.064 25.031,103.751 24.664,103.289C24.039,102.504 23.752,101.177 23.409,99.281L20.632,98.838Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M17.728,43.711L20.072,43.757C20.379,43.764 20.603,44.019 20.57,44.326L19.96,50.016C19.927,50.324 19.651,50.569 19.344,50.562L17,50.516C16.692,50.509 16.469,50.254 16.501,49.947L17.111,44.257C17.144,43.949 17.419,43.704 17.728,43.711Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M14.525,50.043L15.016,50.769C18.226,50.747 21.609,46.203 16.96,46.965C15.765,47.517 14.906,48.544 14.083,49.287" /> + <path + android:fillColor="#3F3F43" + android:pathData="M21.942,38.79C21.7,38.606 21.501,38.471 21.229,38.311C20.915,38.125 20.694,38.023 20.347,37.877C20.319,37.864 20.256,37.877 20.237,37.913C19.945,37.84 19.713,37.709 19.471,37.484C18.961,37.008 18.635,36.163 18.806,35.302C18.899,34.836 19.077,34.643 19.175,34.524C21.382,31.854 19.929,27.606 19.929,27.606C19.929,27.606 18.689,30.229 14.012,32.025C14.012,32.025 14.205,33.245 14.892,34.107C14.942,34.171 15.457,34.638 15.476,35.37C15.493,36.089 15.495,36.817 15.006,37.918C14.41,39.262 12.894,39.586 11.743,39.125C10.884,38.78 10.541,37.918 10.501,37.068C10.501,37.068 12.842,37.206 12.291,34.583C11.879,32.611 11.387,28.304 13.326,25.813C13.354,25.779 13.381,25.744 13.409,25.71C13.428,25.684 13.45,25.658 13.472,25.634C13.512,25.587 13.553,25.541 13.594,25.496C13.634,25.455 13.673,25.413 13.713,25.372C14.321,24.753 15.121,24.288 16.167,24.06C16.167,24.06 20.158,23.509 20.845,25.856C20.845,25.856 21.945,26.41 22.085,28.481C22.222,30.553 21.877,31.311 21.808,32.901C21.726,34.833 21.671,36.768 23.735,37.182C23.738,37.179 23.81,38.233 21.942,38.79Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M95.222,103.977C95.133,106.863 89.824,106.12 91.081,102.988C92.03,99.073 90.912,100.258 94.518,100.104C94.69,100.434 95.249,103.037 95.222,103.977Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M80.372,102.192C81.355,101.636 84.55,100.449 84.036,99.166L86.805,98.747C86.83,99.24 88.239,102.807 86.538,103.137C85.162,103.407 76.471,105.708 80.372,102.192Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M96.419,58.771C96.419,58.771 96.727,62.164 96.782,62.809C97.286,68.857 95.6,94.977 95.148,98.876C95.117,99.144 94.968,99.967 94.49,100.144C93.513,100.507 92.001,100.144 92.001,100.144C91.729,100.144 91.504,99.93 91.491,99.659C91.288,95.284 89.243,69.983 88.935,67.645C87.733,71.998 87.519,93.848 87.019,99.354C86.992,99.664 86.73,99.901 86.418,99.901C86.418,99.901 85.162,100.142 84.563,99.901C83.929,99.646 83.855,98.856 83.827,98.616C82.529,87.427 80.309,69.073 81.611,57.84L86.195,59.817L96.419,58.771Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M92.795,28.889C92.795,28.889 85.723,28.409 85.719,28.416L85.585,28.858C85.171,27.519 83.561,23.782 85.636,22.6C86.925,19.559 94.105,20.936 93.877,24.978C93.959,25.453 92.795,28.889 92.795,28.889Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M78.689,56.14L77.745,56.212C77.041,57.74 76.881,60.203 78.025,61.686C79.174,62.73 80.407,61.019 80.467,60.055C80.647,58.536 80.118,57.207 79.673,56.056L78.689,56.14Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M77.972,44.165C78.17,43.08 78.421,42.023 78.731,41.008C79.407,38.482 79.679,36.791 81.712,35.443C82.555,34.884 83.655,34.495 85.162,33.993C85.707,33.811 86.088,33.481 86.202,33.03C86.344,32.479 90.59,32.713 91.645,32.824C91.8,32.84 91.801,32.702 91.867,32.843C92.061,33.268 92.424,33.811 93.332,34.174C94.559,34.663 97.533,35.627 98.661,37.612C100.413,40.698 103.318,53.022 101.332,54.365C100.44,54.969 97.148,55.469 96.615,55.56C96.611,57.149 96.6,59.366 96.455,59.699C94.255,61.156 87.246,61.525 81.52,60.671C79.131,60.671 80.127,59.699 80.127,56.758C80.127,57.836 78.393,57.651 77.694,57.465C77.29,57.356 76.954,57.017 76.941,56.598C76.81,51.935 77.235,48.212 77.972,44.165ZM96.742,51.614C97.326,51.391 97.582,51.496 97.871,51.391C97.913,50.034 96.582,44.452 96.419,43.961C96.419,43.984 96.638,47.661 96.742,51.614Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M88.817,48.613H91.315C91.64,48.613 91.905,48.876 91.905,49.202V55.275C91.905,55.6 91.642,55.864 91.315,55.864H88.862C88.537,55.864 88.272,55.602 88.272,55.275V49.209C88.272,48.881 88.534,48.615 88.857,48.615" /> + <path + android:fillColor="#3F3F43" + android:pathData="M89.082,34.556C87.972,34.337 86.747,33.659 86.228,32.978C86.398,32.34 86.674,31.39 86.654,31.263C86.934,31.563 91.226,31.57 91.509,31.263C91.489,31.39 91.765,32.34 91.936,32.978C91.413,33.659 90.191,34.335 89.082,34.556Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M92.811,29.04V28.96C93.368,28.572 93.719,27.871 93.701,27.334C93.703,27.05 93.588,26.374 93.271,26.671C93.187,27.033 92.913,28.297 92.641,28.498C92.869,20.596 87.762,27.9 86.319,24.565C85.246,24.924 86.175,27.987 85.823,28.487C85.533,28.278 85.342,27.044 85.267,26.671C85.202,26.622 85.142,26.582 85.091,26.602C84.501,27.233 84.984,28.44 85.73,28.964C85.676,30.897 87.905,33.139 89.271,32.846C90.629,33.136 92.833,30.939 92.811,29.003" /> + <path + android:fillColor="#3F3F43" + android:pathData="M92.606,28.215C92.608,28.14 92.609,28.072 92.609,28.001C92.611,28.079 92.611,28.159 92.611,28.24C92.609,28.231 92.608,28.224 92.606,28.215Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M94.445,53.869L94.454,52.995C93.056,52.2 90.721,51.843 89.213,52.807C88.121,53.813 89.652,55.127 90.567,55.263C92.001,55.56 93.31,55.168 94.447,54.84" /> + <path + android:fillColor="#3F3F43" + android:pathData="M37.907,102.23C37.821,105.053 32.717,104.326 33.925,101.262C34.838,97.432 33.763,98.591 37.229,98.44C37.395,98.763 37.933,101.309 37.907,102.23Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M23.629,100.483C24.574,99.939 27.646,98.777 27.152,97.522L29.814,97.113C29.838,97.595 31.193,101.084 29.557,101.407C28.234,101.671 19.878,103.923 23.629,100.483Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M39.057,58.003C39.057,58.003 39.354,61.322 39.406,61.953C39.891,67.87 38.27,93.425 37.835,97.239C37.805,97.501 37.662,98.306 37.203,98.48C36.264,98.835 34.81,98.48 34.81,98.48C34.548,98.48 34.332,98.271 34.319,98.005C34.124,93.724 32.158,68.971 31.862,66.684C30.706,70.943 30.5,92.32 30.02,97.707C29.994,98.01 29.743,98.242 29.442,98.242C29.442,98.242 28.234,98.478 27.658,98.242C27.049,97.992 26.978,97.219 26.951,96.985C25.703,86.038 23.568,68.081 24.82,57.092L29.228,59.026L39.057,58.003Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M35.573,28.767C35.573,28.767 28.774,28.298 28.77,28.305L28.641,28.737C28.243,27.427 26.694,23.771 28.69,22.615C29.929,19.639 36.833,20.987 36.613,24.941C36.692,25.406 35.573,28.767 35.573,28.767Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M22.011,55.428L21.104,55.499C20.426,56.994 20.273,59.404 21.372,60.854C22.477,61.875 23.663,60.201 23.72,59.258C23.893,57.772 23.385,56.473 22.957,55.347L22.011,55.428Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M21.322,43.713C21.512,42.651 21.753,41.618 22.051,40.625C22.701,38.153 22.962,36.498 24.918,35.179C25.728,34.633 26.785,34.252 28.234,33.761C28.758,33.583 29.125,33.261 29.235,32.819C29.371,32.28 33.454,32.509 34.468,32.617C34.616,32.633 34.618,32.498 34.681,32.637C34.868,33.051 35.217,33.583 36.089,33.938C37.269,34.417 40.129,35.36 41.213,37.302C42.897,40.321 45.69,52.379 43.78,53.692C42.923,54.283 39.759,54.772 39.245,54.861C39.242,56.416 39.231,58.584 39.092,58.911C38.003,61.455 28.645,62.019 24.733,59.139C24.308,58.827 24.848,48.075 25.536,43.281C25.461,43.6 25.38,43.947 25.298,44.314C24.872,46.217 24.569,48.478 24.219,50.783C23.875,53.052 23.696,55.748 23.394,56.033C22.413,56.957 21.727,56.907 21.055,56.724C20.665,56.618 20.342,56.286 20.33,55.877C20.205,51.315 20.613,47.673 21.322,43.713ZM39.368,51.001C39.93,50.783 40.176,50.886 40.453,50.783C40.494,49.455 39.214,43.993 39.057,43.513C39.057,43.536 39.268,47.134 39.368,51.001Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M31.748,48.064H34.15C34.462,48.064 34.717,48.321 34.717,48.641V54.582C34.717,54.9 34.464,55.159 34.15,55.159H31.792C31.479,55.159 31.224,54.902 31.224,54.582V48.648C31.224,48.327 31.476,48.066 31.786,48.066" /> + <path + android:fillColor="#3F3F43" + android:pathData="M32.003,34.312C30.937,34.097 29.758,33.434 29.259,32.768C29.423,32.143 29.688,31.214 29.669,31.09C29.938,31.383 34.065,31.39 34.337,31.09C34.318,31.214 34.583,32.143 34.747,32.768C34.244,33.434 33.07,34.096 32.003,34.312Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M35.589,28.914V28.837C36.124,28.457 36.461,27.771 36.444,27.246C36.446,26.968 36.336,26.306 36.03,26.597C35.95,26.952 35.686,28.188 35.424,28.384C35.644,20.653 30.734,27.799 29.346,24.537C28.315,24.888 29.208,27.884 28.87,28.374C28.59,28.17 28.407,26.962 28.336,26.597C28.273,26.549 28.215,26.51 28.166,26.53C27.599,27.147 28.063,28.328 28.781,28.84C28.728,30.732 30.872,32.925 32.185,32.638C33.49,32.922 35.61,30.773 35.589,28.879" /> + <path + android:fillColor="#E4B489" + android:pathData="M35.391,28.108C35.393,28.035 35.395,27.968 35.395,27.899C35.397,27.975 35.397,28.053 35.397,28.133C35.395,28.124 35.393,28.117 35.391,28.108Z" /> + <path + android:fillColor="#3F3F43" + android:pathData="M37.159,53.207L37.168,52.352C35.824,51.573 33.579,51.224 32.129,52.167C31.08,53.152 32.551,54.437 33.431,54.57C34.81,54.861 36.068,54.478 37.161,54.157" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M61.232,133.102C61.093,137.727 52.786,136.536 54.752,131.515C56.238,125.24 54.488,127.14 60.13,126.893C60.4,127.422 61.275,131.594 61.232,133.102Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M37.996,130.241C39.533,129.349 44.533,127.446 43.729,125.389L48.062,124.718C48.102,125.508 50.306,131.226 47.644,131.755C45.491,132.188 31.891,135.878 37.996,130.241Z" /> + <path + android:fillColor="#8C8C98" + android:pathData="M63.105,60.632C63.105,60.632 63.588,66.071 63.673,67.106C64.463,76.801 61.823,118.674 61.116,124.923C61.068,125.353 60.835,126.672 60.088,126.957C58.559,127.538 56.192,126.957 56.192,126.957C55.766,126.957 55.414,126.614 55.394,126.178C55.076,119.165 51.877,78.605 51.394,74.857C49.513,81.836 49.178,116.863 48.397,125.69C48.354,126.187 47.945,126.567 47.456,126.567C47.456,126.567 45.49,126.954 44.553,126.567C43.561,126.158 43.445,124.891 43.402,124.508C41.371,106.57 37.896,77.147 39.933,59.139L47.107,62.309L63.105,60.632Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M57.434,12.729C57.434,12.729 46.368,11.959 46.362,11.97L46.152,12.679C45.504,10.532 42.984,4.541 46.232,2.647C48.249,-2.228 59.485,-0.02 59.127,6.459C59.255,7.22 57.434,12.729 57.434,12.729Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M35.362,56.414L33.885,56.53C32.783,58.979 32.533,62.928 34.323,65.305C36.121,66.978 38.05,64.235 38.144,62.69C38.425,60.255 37.598,58.125 36.902,56.28L35.362,56.414Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M67.309,56.414L65.832,56.53C64.73,58.979 64.48,62.928 66.27,65.305C68.068,66.978 69.997,64.235 70.091,62.69C70.372,60.255 69.545,58.125 68.849,56.28L67.309,56.414Z" /> + <path + android:fillColor="#C66A61" + android:pathData="M34.24,37.219C34.55,35.479 34.942,33.785 35.428,32.158C36.485,28.108 36.911,25.397 40.093,23.235C41.411,22.34 43.132,21.716 45.491,20.911C46.343,20.62 46.939,20.091 47.118,19.368C47.34,18.485 53.985,18.86 55.636,19.037C55.877,19.063 55.88,18.842 55.982,19.069C56.286,19.749 56.855,20.62 58.275,21.201C60.196,21.986 64.849,23.531 66.614,26.713C69.355,31.661 70.926,56.135 69.1,57.504C68.015,58.318 66.362,58.318 65.449,57.504C64.536,53.397 64.08,43.813 62.254,36.51C64.08,55.679 63.389,61.586 63.162,62.121C61.389,66.29 46.158,67.214 39.791,62.496C39.101,61.984 39.979,44.366 41.098,36.51C40.976,37.033 40.845,37.602 40.712,38.204C40.019,41.321 39.524,45.026 38.956,48.803C38.396,52.521 38.104,56.938 37.612,57.405C36.016,58.919 34.899,58.838 33.805,58.539C33.172,58.364 32.646,57.821 32.626,57.15C32.422,49.674 33.087,43.707 34.24,37.219Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M51.624,21.814C49.888,21.463 47.971,20.376 47.158,19.284C47.425,18.261 47.857,16.738 47.826,16.535C48.263,17.014 54.979,17.026 55.423,16.535C55.391,16.738 55.823,18.261 56.09,19.284C55.272,20.376 53.36,21.46 51.624,21.814Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M57.46,12.97V12.842C58.332,12.221 58.881,11.096 58.852,10.236C58.855,9.78 58.676,8.696 58.179,9.173C58.048,9.754 57.619,11.779 57.193,12.101C57.551,-0.566 49.559,11.143 47.3,5.797C45.621,6.372 47.076,11.282 46.525,12.084C46.07,11.75 45.772,9.771 45.656,9.173C45.553,9.094 45.459,9.03 45.38,9.062C44.457,10.073 45.212,12.009 46.38,12.848C46.295,15.948 49.784,19.542 51.92,19.072C54.045,19.536 57.494,16.015 57.46,12.912" /> + <path + android:fillColor="#DBF3FF" + android:pathData="M55.409,17.343C57.976,15.417 57.793,10.428 57.69,10.497C53.88,13.06 53.084,10.754 51.957,10.754C50.435,11.053 48.563,12.779 46.32,10.042C46.32,11.066 45.369,15.061 49.02,17.799C51.358,19.552 53.583,18.712 55.409,17.343Z" /> + <path + android:fillColor="#E4B489" + android:pathData="M57.139,11.648C57.141,11.529 57.144,11.418 57.144,11.305C57.147,11.43 57.147,11.558 57.147,11.688C57.144,11.674 57.141,11.662 57.139,11.648Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_negative.xml b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_negative.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2e42253f9a95a911d9575aa02a4ff0998d26b8d --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_negative.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/colorStatisticsTrendNegative" /> +</shape> diff --git a/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_neutral.xml b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_neutral.xml new file mode 100644 index 0000000000000000000000000000000000000000..dad10fadf9e1b4c05a5ca05ba533b53884ecb0a3 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_neutral.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/colorStatisticsTrendNeutral" /> +</shape> diff --git a/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_positive.xml b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_positive.xml new file mode 100644 index 0000000000000000000000000000000000000000..11dab35c82839ead62623e3f2337c8bb7593bbb8 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/bg_statistics_trend_positive.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/colorStatisticsTrendPositive" /> +</shape> diff --git a/Corona-Warn-App/src/main/res/drawable/circle_ripple.xml b/Corona-Warn-App/src/main/res/drawable/circle_ripple.xml new file mode 100644 index 0000000000000000000000000000000000000000..55792dede3c7d1e933a6b30c2030768c9725a745 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/circle_ripple.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item + android:id="@android:id/mask" + android:bottom="@dimen/spacing_mega_tiny" + android:left="@dimen/spacing_mega_tiny" + android:right="@dimen/spacing_mega_tiny" + android:top="@dimen/spacing_mega_tiny"> + <shape android:shape="oval"> + <solid android:color="@color/colorSurface2Pressed" /> + </shape> + </item> +</ripple> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_7_day_r_value.xml b/Corona-Warn-App/src/main/res/drawable/ic_7_day_r_value.xml new file mode 100644 index 0000000000000000000000000000000000000000..3d51e72011f0b1d4b402eed791a503110b74106e --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_7_day_r_value.xml @@ -0,0 +1,101 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="74dp" + android:height="142dp" + android:viewportWidth="74" + android:viewportHeight="142"> + <path + android:fillColor="#E2E2E2" + android:pathData="M55.12,20.069L54.909,25.971H60.336L59.536,20.069H55.12Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M62.162,16.17C61.699,25.247 52.666,23.622 52.888,14.505C53.349,5.429 62.384,7.052 62.162,16.17Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M50.54,50.994C46.734,66.255 50.388,88.185 51.565,105.785L54.327,105.875C54.327,105.875 55.912,77.253 56.584,70.744C56.788,68.767 57.455,63.643 57.795,61.805C57.822,61.666 58.115,61.057 58.151,61.767C58.375,66.255 60.39,72.091 60.837,77.028C61.883,88.566 61.652,96.599 62.107,106.122C62.107,106.122 65.31,108.021 65.314,108C65.984,105.343 67.777,83.452 67.777,80.17C67.777,79.497 68.001,64.684 65.091,51.218L50.54,50.994Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M69.568,52.363C67.887,60.189 79.169,61.702 70.998,50.745L69.568,52.363Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M71.842,50.365C71.423,47.403 70.689,42.874 70.015,39.772C68.896,34.61 67.553,26.755 60.166,24.735C58.925,25.936 55.997,25.148 54.999,24.735C54.981,24.728 54.974,24.735 54.954,24.735C48.133,24.802 46.463,31.414 45.391,35.283C44.272,39.323 42.353,45.345 43.217,46.73C44.896,49.423 46.358,47.982 50.381,45.159C50.573,45.024 50.768,44.882 50.965,44.741C50.902,47.452 50.777,49.896 50.542,50.994C50,53.532 65.764,53.463 65.317,51.667C64.367,47.865 63.973,40.894 63.973,35.508C63.973,35.059 65.09,37.341 65.54,38.874C66.66,42.69 67.62,49.212 69.106,52.453C69.57,53.463 70.228,53.043 71.316,52.666C72.247,52.332 71.896,50.754 71.842,50.365ZM48.525,41.792C48.525,41.792 48.749,41.568 49.644,38.425C50.17,36.581 50.987,33.712 50.987,33.712C50.987,33.712 51.041,37.115 51.016,40.953C49.324,41.466 48.525,41.792 48.525,41.792Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M54.318,104.744L51.435,104.185C51.35,104.952 50.316,106.205 50.887,109.183C50.931,109.421 50.987,111.685 51.063,111.93C51.133,112.157 52.107,112.487 52.107,112.262C52.107,112.036 51.829,109.239 52.33,110.242C52.554,110.691 52.554,112.262 53.674,113.16C54.952,114.186 58.979,114.516 57.479,112.711C55.241,110.018 54.585,105.63 54.41,104.688" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M51.625,4.466C51.625,4.466 52.608,2.264 54.377,2.78C54.377,2.78 55.021,-0.676 59.404,0.119C63.788,0.913 63.714,3.923 63.714,3.923C63.714,3.923 68.39,3.93 67.15,9.462C67.15,9.462 68.258,11.967 66.49,13.241C66.49,13.241 67.11,15.993 64.376,16.305C64.376,16.305 64.161,17.113 63.481,16.974C63.481,16.974 62.274,18.522 61.836,17.476C61.574,16.85 61.632,15.611 61.721,14.718C61.8,13.93 61.567,13.147 61.068,12.534C60.459,11.789 59.546,10.939 58.5,10.867C56.606,10.737 53.998,12.068 53.64,13.446C53.371,14.476 54.133,17.793 52.507,17.802C51.471,17.809 50.972,16.287 50.972,16.287C50.972,16.287 47.959,15.773 48.805,13.378C48.805,13.378 47.285,11.63 48.554,9.549C48.554,9.552 47.101,4.538 51.625,4.466Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M62.064,104.665C62.064,104.665 62.026,106.156 61.735,107.327C61.511,108.225 61.287,110.918 62.183,113.163H63.526C63.526,113.163 63.078,110.694 63.302,109.347C63.362,108.988 63.526,110.918 64.869,112.489C65.487,113.212 66.042,113.311 66.304,113.34C66.785,113.394 69.642,113.387 69.794,113.387C70.242,113.387 70.689,112.489 68.892,111.693C68.41,111.479 67.81,111.071 67.331,110.469C66.517,109.448 66.183,107.718 65.735,105.249L62.064,104.665Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M58.287,32.891L61.343,32.951C61.744,32.96 62.035,33.292 61.992,33.692L61.198,41.1C61.155,41.502 60.795,41.821 60.394,41.812L57.338,41.751C56.938,41.742 56.646,41.41 56.689,41.011L57.484,33.602C57.526,33.2 57.884,32.882 58.287,32.891Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M54.112,41.137L54.753,42.082C58.934,42.052 63.347,36.136 57.287,37.128C55.729,37.846 54.609,39.184 53.537,40.151" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M24.083,113.59C23.966,117.433 16.981,116.444 18.634,112.272C19.883,107.058 18.412,108.637 23.156,108.431C23.383,108.871 24.119,112.337 24.083,113.59Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M4.545,111.212C5.838,110.471 10.042,108.89 9.366,107.181L13.009,106.623C13.042,107.28 14.896,112.031 12.657,112.47C10.847,112.83 -0.588,115.896 4.545,111.212Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M25.657,53.378C25.657,53.378 26.063,57.897 26.135,58.756C26.799,66.812 24.58,101.603 23.985,106.795C23.944,107.152 23.748,108.248 23.12,108.485C21.835,108.968 19.845,108.485 19.845,108.485C19.487,108.485 19.191,108.2 19.174,107.838C18.906,102.011 16.217,68.311 15.811,65.197C14.229,70.995 13.948,100.099 13.291,107.432C13.255,107.845 12.911,108.162 12.5,108.162C12.5,108.162 10.847,108.483 10.059,108.162C9.225,107.821 9.127,106.769 9.091,106.45C7.383,91.546 4.462,67.099 6.175,52.137L12.206,54.771L25.657,53.378Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M20.889,13.575C20.889,13.575 11.585,12.936 11.58,12.945L11.403,13.534C10.859,11.75 8.74,6.773 11.47,5.199C13.166,1.148 22.614,2.983 22.313,8.366C22.42,8.998 20.889,13.575 20.889,13.575Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M2.331,49.872L1.089,49.969C0.162,52.004 -0.048,55.285 1.457,57.259C2.969,58.65 4.591,56.371 4.67,55.087C4.906,53.064 4.211,51.294 3.626,49.761L2.331,49.872Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M1.388,33.923C1.648,32.477 1.978,31.07 2.386,29.718C3.275,26.353 3.633,24.101 6.308,22.305C7.417,21.561 8.864,21.042 10.847,20.373C11.564,20.132 12.065,19.693 12.216,19.092C12.402,18.358 17.989,18.669 19.377,18.816C19.58,18.838 19.583,18.655 19.669,18.843C19.924,19.408 20.402,20.132 21.596,20.615C23.211,21.267 27.124,22.551 28.607,25.194C30.912,29.305 34.734,45.721 32.121,47.509C30.948,48.313 26.617,48.979 25.915,49.1C25.91,51.217 25.896,54.17 25.705,54.614C24.214,58.078 11.408,58.846 6.055,54.925C5.475,54.5 6.213,39.862 7.154,33.334C7.051,33.769 6.941,34.242 6.829,34.742C6.246,37.332 5.831,40.41 5.353,43.548C4.882,46.638 4.636,50.307 4.223,50.696C2.881,51.953 1.942,51.886 1.022,51.637C0.49,51.493 0.048,51.041 0.031,50.483C-0.141,44.272 0.418,39.314 1.388,33.923ZM26.082,43.845C26.851,43.548 27.188,43.688 27.568,43.548C27.623,41.74 25.872,34.305 25.657,33.651C25.657,33.682 25.946,38.58 26.082,43.845Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M15.655,39.847H18.942C19.37,39.847 19.719,40.197 19.719,40.632V48.721C19.719,49.153 19.372,49.506 18.942,49.506H15.715C15.288,49.506 14.939,49.155 14.939,48.721V40.641C14.939,40.204 15.283,39.849 15.708,39.849" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M16.004,21.124C14.545,20.832 12.932,19.929 12.249,19.021C12.474,18.171 12.837,16.906 12.811,16.737C13.178,17.136 18.825,17.146 19.198,16.737C19.172,16.906 19.535,18.171 19.759,19.021C19.071,19.929 17.464,20.829 16.004,21.124Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M20.911,13.776V13.669C21.644,13.153 22.105,12.218 22.081,11.504C22.084,11.125 21.933,10.224 21.515,10.62C21.405,11.103 21.045,12.786 20.686,13.054C20.987,2.529 14.268,12.257 12.369,7.815C10.957,8.293 12.18,12.373 11.717,13.039C11.334,12.762 11.084,11.118 10.986,10.62C10.9,10.555 10.821,10.502 10.754,10.529C9.978,11.369 10.613,12.976 11.595,13.674C11.523,16.25 14.456,19.236 16.253,18.845C18.039,19.231 20.939,16.305 20.911,13.727" /> + <path + android:fillColor="#E4B489" + android:pathData="M20.641,12.677C20.643,12.578 20.646,12.487 20.646,12.393C20.648,12.497 20.648,12.603 20.648,12.711C20.646,12.699 20.643,12.69 20.641,12.677Z" /> + <path + android:fillColor="#E2E2E2" + android:pathData="M23.06,46.848L23.072,45.684C21.233,44.624 18.161,44.149 16.176,45.433C14.741,46.773 16.754,48.523 17.958,48.704C19.845,49.1 21.567,48.578 23.063,48.141" /> + <path + android:fillColor="#F6B893" + android:pathData="M33.957,27.638L33.277,34.412L31.896,35.505L30.515,36.324L32.449,40.423L43.223,40.696L41.841,36.324L42.118,34.138L41.275,33.772C40.598,33.477 40.123,32.857 40.024,32.13L39.408,27.635H33.957V27.638Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M42.648,22.892C42.076,33.943 30.929,31.964 31.202,20.865C31.772,9.814 42.922,11.79 42.648,22.892Z" /> + <path + android:fillColor="#ffffff" + android:pathData="M41.898,24.024C39.271,24.856 36.27,25.585 30.642,24.024C29.861,24.891 29.517,27.457 32.518,28.705C35.52,29.953 39.084,29.225 40.491,28.705C42.054,26.798 44.524,23.192 41.898,24.024Z" + android:strokeWidth="1" + android:strokeColor="#ffffff" /> + <path + android:fillColor="#8C8C98" + android:pathData="M28.304,65.29C23.608,83.871 28.116,110.571 29.569,131.999L32.979,132.108C32.979,132.108 34.935,97.26 35.763,89.336C36.015,86.929 36.838,81.784 37.258,79.546C37.291,79.376 37.653,78.636 37.697,79.499C37.973,84.964 40.46,90.976 41.012,96.987C42.303,111.035 42.018,120.815 42.579,132.409C42.579,132.409 46.532,134.721 46.538,134.696C47.364,131.461 49.576,104.808 49.576,100.813C49.576,99.993 50.654,82.923 46.261,65.563L28.304,65.29Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M50.842,66.539C48.248,75.941 62.053,78.526 52.715,64.667L50.842,66.539Z" /> + <path + android:fillColor="#C66A61" + android:pathData="M19.268,60.098C21.34,63.377 23.144,61.622 28.108,58.185C28.346,58.021 28.584,57.849 28.83,57.677C28.752,60.978 28.597,63.953 28.307,65.289C27.639,68.38 47.093,68.295 46.541,66.109C45.369,61.48 44.883,52.993 44.883,46.435C44.883,45.889 45.806,48.643 46.264,50.534C47.43,55.324 48.278,62.3 50.113,66.246C50.685,67.475 51.497,66.964 52.84,66.505C53.989,66.098 53.525,64.18 53.489,63.702C53.229,60.242 52.619,55.4 51.787,51.624C50.511,45.812 49.549,37.194 42.115,33.986C41.993,33.934 41.853,34.024 41.855,34.158C41.927,36.609 39.3,40.147 37.974,40.147C35.487,40.147 31.344,36.595 30.769,33.726C24.854,35.434 23.097,42.079 21.951,46.159C20.569,51.08 18.202,58.412 19.268,60.098ZM26.012,53.687C26.205,53.214 26.564,52.19 27.202,49.987L28.515,45.449C28.573,45.246 28.874,45.285 28.876,45.495C28.893,47.069 29.197,48.509 29.175,51.624C27.788,52.739 26.719,53.753 26.247,53.925C26.094,53.982 25.951,53.835 26.012,53.687Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M33.076,130.468L29.41,130.053C29.305,130.988 28.028,132.512 28.733,136.138C28.788,136.428 28.857,139.185 28.951,139.483C29.037,139.759 30.239,140.161 30.239,139.887C30.239,139.611 29.896,136.207 30.515,137.428C30.791,137.975 30.791,139.887 32.172,140.98C33.75,142.229 38.72,142.631 36.869,140.434C34.106,137.155 33.28,131.955 33.065,130.807" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M42.537,130.647C42.537,130.647 42.479,132.45 42.117,133.876C41.841,134.969 41.565,138.249 42.67,140.981H44.327C44.327,140.981 43.775,137.975 44.051,136.336C44.126,135.899 44.327,138.249 45.985,140.161C46.748,141.041 47.433,141.161 47.756,141.197C48.35,141.262 51.875,141.254 52.063,141.254C52.615,141.254 53.168,140.161 50.95,139.191C50.356,138.932 49.615,138.434 49.024,137.702C48.018,136.459 47.557,134.355 47.005,131.349L42.537,130.647Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M33.541,50.633L34.668,51.465C39.535,49.76 42.299,41.192 35.631,44.754C34.103,46.202 33.335,48.188 32.474,49.729" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M44.646,35.448C44.256,35.155 43.936,34.942 43.499,34.688C42.994,34.393 42.637,34.232 42.079,34C42.035,33.98 41.933,34 41.902,34.057C41.433,33.942 41.06,33.735 40.67,33.376C39.85,32.622 39.325,31.283 39.601,29.917C39.75,29.179 40.038,28.873 40.195,28.685C43.745,24.452 41.408,17.716 41.408,17.716C41.408,17.716 39.413,21.875 31.888,24.723C31.888,24.723 32.2,26.657 33.305,28.024C33.385,28.125 34.214,28.865 34.244,30.027C34.272,31.166 34.275,32.319 33.487,34.065C32.529,36.196 30.089,36.71 28.238,35.978C26.857,35.431 26.305,34.065 26.241,32.718C26.241,32.718 30.007,32.937 29.12,28.778C28.457,25.652 27.667,18.823 30.786,14.875C30.83,14.82 30.874,14.765 30.918,14.711C30.948,14.67 30.985,14.629 31.02,14.59C31.084,14.517 31.15,14.443 31.216,14.372C31.28,14.306 31.344,14.241 31.407,14.175C32.385,13.194 33.673,12.456 35.355,12.096C35.355,12.096 41.775,11.221 42.88,14.943C42.88,14.943 44.651,15.82 44.875,19.105C45.096,22.389 44.541,23.591 44.43,26.111C44.298,29.174 44.209,32.243 47.53,32.898C47.535,32.893 47.652,34.565 44.646,35.448Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_illustration_statistics_explanation.xml b/Corona-Warn-App/src/main/res/drawable/ic_illustration_statistics_explanation.xml new file mode 100644 index 0000000000000000000000000000000000000000..b478e32a85d8e29a543f5704f90625f053e8771f --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_illustration_statistics_explanation.xml @@ -0,0 +1,37 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="235dp" + android:height="148dp" + android:viewportWidth="235" + android:viewportHeight="148"> + <path + android:pathData="M143.346,145.276H93.238C88.136,145.274 84.001,141.138 84,136.036L84.013,11.057C84.02,5.96 88.154,1.832 93.251,1.832L143.36,1.833C148.453,1.842 152.577,5.972 152.58,11.066V136.047C152.578,141.145 148.445,145.277 143.346,145.276Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M143.346,145.276H93.238C88.136,145.274 84.001,141.138 84,136.036L84.013,11.057C84.02,5.96 88.154,1.832 93.251,1.832L143.36,1.833C148.453,1.842 152.577,5.972 152.58,11.066V136.047C152.578,141.145 148.445,145.277 143.346,145.276Z" + android:strokeWidth="3.567" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#4A4A4A"/> + <path + android:pathData="M93.815,11L142.357,11A4,4 0,0 1,146.357 15L146.357,24A4,4 0,0 1,142.357 28L93.815,28A4,4 0,0 1,89.815 24L89.815,15A4,4 0,0 1,93.815 11z" + android:fillColor="#E7E7E7"/> + <path + android:pathData="M93.815,36L142.815,36A4,4 0,0 1,146.815 40L146.815,70A4,4 0,0 1,142.815 74L93.815,74A4,4 0,0 1,89.815 70L89.815,40A4,4 0,0 1,93.815 36z" + android:fillColor="#E7E7E7"/> + <path + android:pathData="M93.815,113L142.815,113A4,4 0,0 1,146.815 117L146.815,135A4,4 0,0 1,142.815 139L93.815,139A4,4 0,0 1,89.815 135L89.815,117A4,4 0,0 1,93.815 113z" + android:fillColor="#E7E7E7"/> + <path + android:pathData="M93.815,79L119.815,79A4,4 0,0 1,123.815 83L123.815,104A4,4 0,0 1,119.815 108L93.815,108A4,4 0,0 1,89.815 104L89.815,83A4,4 0,0 1,93.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M130.815,79L156.815,79A4,4 0,0 1,160.815 83L160.815,104A4,4 0,0 1,156.815 108L130.815,108A4,4 0,0 1,126.815 104L126.815,83A4,4 0,0 1,130.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M167.815,79L193.815,79A4,4 0,0 1,197.815 83L197.815,104A4,4 0,0 1,193.815 108L167.815,108A4,4 0,0 1,163.815 104L163.815,83A4,4 0,0 1,167.815 79z" + android:fillColor="#007FAD"/> + <path + android:pathData="M204.815,79L230.815,79A4,4 0,0 1,234.815 83L234.815,104A4,4 0,0 1,230.815 108L204.815,108A4,4 0,0 1,200.815 104L200.815,83A4,4 0,0 1,204.815 79z" + android:fillColor="#007FAD"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_info.xml b/Corona-Warn-App/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..8844a863866db7381c1bf48d1238196fdb743be6 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_info.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="21" + android:viewportHeight="21"> + <path + android:fillColor="#747576" + android:pathData="M10.5,20.8813C16.1294,20.8813 20.7847,16.2261 20.7847,10.5967C20.7847,4.957 16.1294,0.312 10.4897,0.312C4.8604,0.312 0.2153,4.957 0.2153,10.5967C0.2153,16.2261 4.8706,20.8813 10.5,20.8813ZM10.5,19.5483C5.5371,19.5483 1.5483,15.5493 1.5483,10.5967C1.5483,5.6338 5.5371,1.6348 10.4897,1.6348C15.4526,1.6348 19.4517,5.6338 19.4619,10.5967C19.4619,15.5493 15.4629,19.5483 10.5,19.5483ZM10.4385,6.731C11.0742,6.731 11.5664,6.2388 11.5664,5.6133C11.5664,4.9878 11.0742,4.4854 10.4385,4.4854C9.813,4.4854 9.3105,4.9878 9.3105,5.6133C9.3105,6.2388 9.813,6.731 10.4385,6.731ZM8.6645,16.1953H12.8687C13.1968,16.1953 13.4531,15.9492 13.4531,15.6313C13.4531,15.3135 13.1968,15.0571 12.8687,15.0571H11.3921V9.3149C11.3921,8.8945 11.1768,8.6074 10.7769,8.6074H8.7978C8.4697,8.6074 8.2134,8.8638 8.2134,9.1714C8.2134,9.4995 8.4697,9.7456 8.7978,9.7456H10.1411V15.0571H8.6645C8.3364,15.0571 8.0801,15.3135 8.0801,15.6313C8.0801,15.9492 8.3364,16.1953 8.6645,16.1953Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_infection.xml b/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_infection.xml new file mode 100644 index 0000000000000000000000000000000000000000..61dda629c3f9138d7836ea287935470021ea767f --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_infection.xml @@ -0,0 +1,38 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="107dp" + android:height="178dp" + android:viewportWidth="107" + android:viewportHeight="178"> + <path + android:pathData="M146.474,68.276C151.436,94.254 134.38,119.289 108.396,124.249C82.412,129.21 57.37,112.155 52.407,86.177C47.47,60.231 64.501,35.164 90.485,30.204C116.437,25.269 141.511,42.298 146.474,68.276Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M116.046,144.351C114.156,144.974 107.513,147.251 109.933,139.931C113.127,130.388 105.52,124.874 105.52,124.874L99.539,121.336L120.987,117.65C120.987,117.65 116.62,122.526 116.236,126.41C116.041,128.38 116.832,134.13 118.964,135.961C121.096,137.792 125.315,141.332 116.046,144.351Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M49.868,37.372C51.673,35.469 53.404,32.951 55.25,37.291C55.887,38.807 56.759,40.032 57.771,41.043C60.3,43.698 62.523,44.471 66.187,44.512L69.014,44.544L58.39,54.658C58.39,54.658 59.007,51.262 56.11,48.796C54.927,47.766 52.562,45.187 50.381,45.315C48.384,45.348 43.271,44.333 49.868,37.372Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M60.032,86.959C58.637,87.031 57.307,86.274 56.639,85.043C55.528,82.866 54.054,79.32 47.652,78.968C40.363,78.544 31.933,80.532 31.375,80.872C27.146,83.511 24.511,85.349 23.989,75.961C23.497,67.068 26.024,65.42 29.951,69.505C30.805,70.382 31.887,71.025 33.065,71.332C37.297,72.477 50.916,73.88 56.201,69.831C57.814,68.599 62.557,78.206 62.557,78.206C63.883,80.523 60.032,86.959 60.032,86.959Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M42.258,117.024C39.05,111.952 39.224,108.131 45.71,107.742C56.913,107.022 59.267,93.167 59.267,93.167L57.386,87.611L71.83,109.208C71.83,109.208 65.185,107.442 60.7,108.521C58.506,109.022 53.116,112.222 51.492,115.07C49.761,118.108 46.248,123.34 42.258,117.024Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M95.199,118.62C95.199,118.62 87.011,117.718 84.489,127.453C82.024,137.194 81.848,139.513 81.848,139.513C81.848,139.513 85.001,153.965 73.086,150.012C61.172,146.059 64.019,140.577 70.981,137.268C70.981,137.268 80.415,124.16 77.215,115.97C74.015,107.78 74.274,110.322 74.274,110.322L97.261,110.132L95.199,118.62Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M124.026,14.408C125.952,15.002 132.66,16.977 126.424,21.525C118.295,27.474 121.272,36.361 121.272,36.361L124.077,42.687L104.525,33.245C104.525,33.245 110.899,31.803 113.455,28.888C114.72,27.414 117.471,22.269 116.743,19.529C116.039,16.821 114.766,11.507 124.026,14.408Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> + <path + android:pathData="M76.455,38.776C76.455,38.776 83.746,35.619 80.968,26.004C79.642,21.406 77.568,16.521 76.958,14.777C76.473,13.451 75.772,12.245 74.792,11.21C73.066,9.367 70.977,5.635 79.716,3.972C89.876,2.007 89.006,5.605 87.382,8.453C86.733,9.592 86.381,10.909 86.289,12.227C86.03,16.79 87.243,28.739 92.374,32.903C99.169,38.44 97.671,36.364 97.671,36.364L81.17,46.85L76.455,38.776Z" + android:fillColor="#E2E2E2" + android:fillType="evenOdd"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_warnende_personen.xml b/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_warnende_personen.xml new file mode 100644 index 0000000000000000000000000000000000000000..9d74df23223ff6e3eaa4f2505752494020931738 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_main_illustration_warnende_personen.xml @@ -0,0 +1,309 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="128dp" + android:height="152dp" + android:viewportWidth="128" + android:viewportHeight="152"> + <group> + <clip-path + android:pathData="M0,0h128v152h-128z"/> + <path + android:pathData="M46.685,106.15C45.72,99.03 44.955,91.9 43.761,84.793C41.285,70.012 38.142,55.354 35.299,40.651C27.719,40.011 22.423,39.605 22.423,39.605C22.423,39.605 8.44,38.01 8.54,44.376C8.64,50.743 24.444,53.273 24.444,53.273L26.741,53.736L27.949,61.484C27.949,61.484 15.694,77.705 16.794,82.612C17.893,87.518 25.456,85.016 25.456,85.016C25.456,85.016 21.561,98.646 25.171,102.031C27.821,104.518 30.33,103.261 30.33,103.261C30.33,103.261 24.183,109.354 27.31,114.406C28.684,116.622 36.183,116.947 36.183,116.947L48.23,116.031C47.187,112.804 46.848,109.49 46.685,106.15Z" + android:fillColor="#F6B893" + android:fillType="evenOdd"/> + <path + android:pathData="M97.324,134.774L52.842,143.053C48.307,143.901 43.953,140.904 43.102,136.381L22.334,25.438C21.481,20.906 24.477,16.557 29.001,15.71L73.484,7.431C78.018,6.582 82.372,9.58 83.223,14.102L103.991,125.046C104.844,129.578 101.86,133.935 97.324,134.774Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M51.739,144.804C49.59,144.899 47.482,144.302 45.679,143.069C43.485,141.567 42.007,139.292 41.514,136.688L20.745,25.744C19.739,20.349 23.31,15.142 28.708,14.142L73.191,5.862C75.808,5.379 78.458,5.936 80.651,7.438C82.844,8.939 84.322,11.214 84.816,13.819L105.585,124.762C106.591,130.158 103.03,135.362 97.621,136.365L53.137,144.634C52.666,144.727 52.199,144.777 51.739,144.804ZM74.734,8.908C74.42,8.924 74.099,8.962 73.791,9.02L29.309,17.299C25.656,17.982 23.24,21.503 23.925,25.144L44.693,136.088C45.027,137.851 46.022,139.388 47.508,140.406C48.994,141.425 50.783,141.806 52.548,141.474L97.03,133.195C100.683,132.513 103.098,128.991 102.414,125.35L81.645,14.407C81.312,12.643 80.317,11.107 78.831,10.088C77.614,9.24 76.192,8.844 74.734,8.908Z" + android:fillColor="#4A4A4A"/> + <path + android:pathData="M204.434,115.192L149.953,103.926C149.953,103.926 136.627,99.689 132.125,88.147C128.107,77.84 112.942,53.132 107.13,45.989C104.698,41.486 100.85,35.246 97.018,32.559C96.013,31.857 95.242,30.888 94.816,29.761C93.395,26 90.329,18.112 89.477,18.199C88.364,18.306 82.804,18.878 81.943,25.929C81.081,32.98 83.9,45.453 91.014,48.273C91.014,48.273 91.386,49.672 92.029,52.075C95.699,71.475 105.079,121.939 105.117,122.123C106.301,128.124 105.491,128.828 104.715,131.066C118.072,133.717 204.779,171.652 204.779,171.652L204.885,140.452L204.434,115.192Z" + android:fillColor="#F6B893" + android:fillType="evenOdd"/> + <path + android:pathData="M58.197,37.733L58.554,39.857C58.972,39.932 59.318,40.257 59.392,40.698C59.486,41.257 59.105,41.786 58.545,41.88C57.982,41.974 57.452,41.596 57.358,41.038C57.284,40.596 57.505,40.177 57.876,39.97L57.519,37.846C56.637,37.92 55.788,37.708 55.072,37.285L53.815,39.041C54.058,39.386 54.071,39.859 53.811,40.224C53.478,40.685 52.836,40.792 52.371,40.465C51.908,40.135 51.801,39.495 52.131,39.033C52.394,38.67 52.847,38.528 53.257,38.644L54.514,36.888C53.878,36.352 53.402,35.621 53.184,34.77L51.047,35.126C50.974,35.543 50.646,35.888 50.204,35.961C49.642,36.055 49.111,35.678 49.017,35.119C48.924,34.561 49.302,34.032 49.864,33.938C50.307,33.864 50.729,34.083 50.934,34.453L53.071,34.097C52.998,33.221 53.21,32.376 53.636,31.662L51.874,30.41C51.524,30.653 51.05,30.666 50.683,30.408C50.22,30.078 50.112,29.438 50.442,28.976C50.775,28.516 51.417,28.409 51.882,28.736C52.247,28.996 52.39,29.448 52.272,29.853L54.035,31.104C54.573,30.472 55.306,29.997 56.165,29.78L55.808,27.656C55.39,27.581 55.044,27.256 54.97,26.814C54.876,26.256 55.254,25.726 55.817,25.633C56.377,25.539 56.91,25.916 57.003,26.475C57.078,26.916 56.856,27.336 56.486,27.542L56.842,29.666C57.844,29.583 58.804,29.866 59.575,30.415L64.366,23.729C61.791,21.899 58.512,21.051 55.142,21.614C48.402,22.739 43.85,29.088 44.977,35.794C46.103,42.5 52.479,47.024 59.22,45.899C62.59,45.336 65.412,43.469 67.25,40.902L60.536,36.138C59.986,36.907 59.171,37.487 58.197,37.733Z"> + <aapt:attr name="android:fillColor"> + <gradient + android:startY="21.9124" + android:startX="53.3534" + android:endY="46.201" + android:endX="57.4092" + android:type="linear"> + <item android:offset="0" android:color="#FFBDE0F9"/> + <item android:offset="0.003906" android:color="#FFBCE0F9"/> + <item android:offset="0.007812" android:color="#FFBCE0F9"/> + <item android:offset="0.011719" android:color="#FFBBDFF8"/> + <item android:offset="0.015625" android:color="#FFBADFF8"/> + <item android:offset="0.019531" android:color="#FFB9DFF8"/> + <item android:offset="0.023437" android:color="#FFB9DFF8"/> + <item android:offset="0.027344" android:color="#FFB8DEF8"/> + <item android:offset="0.03125" android:color="#FFB7DEF8"/> + <item android:offset="0.035156" android:color="#FFB6DEF7"/> + <item android:offset="0.039062" android:color="#FFB6DEF7"/> + <item android:offset="0.042969" android:color="#FFB5DDF7"/> + <item android:offset="0.046875" android:color="#FFB4DDF7"/> + <item android:offset="0.050781" android:color="#FFB3DDF7"/> + <item android:offset="0.054687" android:color="#FFB3DDF7"/> + <item android:offset="0.058594" android:color="#FFB2DCF7"/> + <item android:offset="0.0625" android:color="#FFB1DCF7"/> + <item android:offset="0.066406" android:color="#FFB0DCF6"/> + <item android:offset="0.070312" android:color="#FFB0DCF6"/> + <item android:offset="0.074219" android:color="#FFAFDCF6"/> + <item android:offset="0.078125" android:color="#FFAEDBF6"/> + <item android:offset="0.082031" android:color="#FFAEDBF6"/> + <item android:offset="0.085937" android:color="#FFADDBF6"/> + <item android:offset="0.089844" android:color="#FFACDBF5"/> + <item android:offset="0.09375" android:color="#FFACDBF5"/> + <item android:offset="0.097656" android:color="#FFABDAF5"/> + <item android:offset="0.101562" android:color="#FFAADAF5"/> + <item android:offset="0.105469" android:color="#FFA9DAF5"/> + <item android:offset="0.109375" android:color="#FFA9DAF5"/> + <item android:offset="0.113281" android:color="#FFA8D9F4"/> + <item android:offset="0.117187" android:color="#FFA7D9F4"/> + <item android:offset="0.121094" android:color="#FFA6D9F4"/> + <item android:offset="0.125" android:color="#FFA6D9F4"/> + <item android:offset="0.128906" android:color="#FFA5D8F4"/> + <item android:offset="0.132812" android:color="#FFA4D8F4"/> + <item android:offset="0.136719" android:color="#FFA3D8F3"/> + <item android:offset="0.140625" android:color="#FFA3D8F3"/> + <item android:offset="0.144531" android:color="#FFA2D7F3"/> + <item android:offset="0.148437" android:color="#FFA2D7F3"/> + <item android:offset="0.152344" android:color="#FFA1D7F3"/> + <item android:offset="0.15625" android:color="#FFA0D7F3"/> + <item android:offset="0.160156" android:color="#FF9FD7F3"/> + <item android:offset="0.164062" android:color="#FF9FD7F3"/> + <item android:offset="0.167969" android:color="#FF9ED6F2"/> + <item android:offset="0.171875" android:color="#FF9DD6F2"/> + <item android:offset="0.175781" android:color="#FF9CD6F2"/> + <item android:offset="0.179687" android:color="#FF9CD6F2"/> + <item android:offset="0.183594" android:color="#FF9BD5F2"/> + <item android:offset="0.1875" android:color="#FF9AD5F2"/> + <item android:offset="0.191406" android:color="#FF99D5F1"/> + <item android:offset="0.195312" android:color="#FF99D5F1"/> + <item android:offset="0.199219" android:color="#FF98D4F1"/> + <item android:offset="0.203125" android:color="#FF97D4F1"/> + <item android:offset="0.207031" android:color="#FF96D4F1"/> + <item android:offset="0.210937" android:color="#FF96D4F1"/> + <item android:offset="0.214844" android:color="#FF95D3F0"/> + <item android:offset="0.21875" android:color="#FF95D3F0"/> + <item android:offset="0.222656" android:color="#FF94D3F0"/> + <item android:offset="0.226562" android:color="#FF93D3F0"/> + <item android:offset="0.230469" android:color="#FF92D3F0"/> + <item android:offset="0.234375" android:color="#FF92D2F0"/> + <item android:offset="0.238281" android:color="#FF91D2EF"/> + <item android:offset="0.242187" android:color="#FF90D2EF"/> + <item android:offset="0.246094" android:color="#FF8FD2EF"/> + <item android:offset="0.25" android:color="#FF8FD2EF"/> + <item android:offset="0.253906" android:color="#FF8ED1EF"/> + <item android:offset="0.257812" android:color="#FF8DD1EF"/> + <item android:offset="0.261719" android:color="#FF8CD1EF"/> + <item android:offset="0.265625" android:color="#FF8CD1EF"/> + <item android:offset="0.269531" android:color="#FF8BD0EE"/> + <item android:offset="0.273437" android:color="#FF8AD0EE"/> + <item android:offset="0.277344" android:color="#FF8AD0EE"/> + <item android:offset="0.28125" android:color="#FF89D0EE"/> + <item android:offset="0.285156" android:color="#FF88CFEE"/> + <item android:offset="0.289062" android:color="#FF88CFEE"/> + <item android:offset="0.292969" android:color="#FF87CFED"/> + <item android:offset="0.296875" android:color="#FF86CFED"/> + <item android:offset="0.300781" android:color="#FF85CEED"/> + <item android:offset="0.304687" android:color="#FF85CEED"/> + <item android:offset="0.308594" android:color="#FF84CEED"/> + <item android:offset="0.3125" android:color="#FF83CEED"/> + <item android:offset="0.316406" android:color="#FF82CEEC"/> + <item android:offset="0.320312" android:color="#FF82CEEC"/> + <item android:offset="0.324219" android:color="#FF81CDEC"/> + <item android:offset="0.328125" android:color="#FF80CDEC"/> + <item android:offset="0.332031" android:color="#FF80CCEB"/> + <item android:offset="0.335937" android:color="#FF81CBEA"/> + <item android:offset="0.339844" android:color="#FF81CAE8"/> + <item android:offset="0.34375" android:color="#FF81C9E7"/> + <item android:offset="0.347656" android:color="#FF81C8E6"/> + <item android:offset="0.351562" android:color="#FF82C7E4"/> + <item android:offset="0.355469" android:color="#FF82C6E3"/> + <item android:offset="0.359375" android:color="#FF83C5E2"/> + <item android:offset="0.363281" android:color="#FF83C4E0"/> + <item android:offset="0.367187" android:color="#FF83C3DF"/> + <item android:offset="0.371094" android:color="#FF83C2DD"/> + <item android:offset="0.375" android:color="#FF84C0DC"/> + <item android:offset="0.378906" android:color="#FF84BFDA"/> + <item android:offset="0.382812" android:color="#FF85BED9"/> + <item android:offset="0.386719" android:color="#FF85BDD8"/> + <item android:offset="0.390625" android:color="#FF85BCD7"/> + <item android:offset="0.394531" android:color="#FF85BBD5"/> + <item android:offset="0.398437" android:color="#FF86BAD4"/> + <item android:offset="0.402344" android:color="#FF86B9D2"/> + <item android:offset="0.40625" android:color="#FF87B8D1"/> + <item android:offset="0.410156" android:color="#FF87B7D0"/> + <item android:offset="0.414062" android:color="#FF88B5CE"/> + <item android:offset="0.417969" android:color="#FF88B4CD"/> + <item android:offset="0.421875" android:color="#FF88B3CC"/> + <item android:offset="0.425781" android:color="#FF88B2CA"/> + <item android:offset="0.429687" android:color="#FF89B1C9"/> + <item android:offset="0.433594" android:color="#FF89B0C7"/> + <item android:offset="0.4375" android:color="#FF8AAFC6"/> + <item android:offset="0.441406" android:color="#FF8AAEC4"/> + <item android:offset="0.445312" android:color="#FF8AADC3"/> + <item android:offset="0.449219" android:color="#FF8AACC2"/> + <item android:offset="0.453125" android:color="#FF8BABC1"/> + <item android:offset="0.457031" android:color="#FF8BAABF"/> + <item android:offset="0.460937" android:color="#FF8CA8BE"/> + <item android:offset="0.464844" android:color="#FF8CA7BC"/> + <item android:offset="0.46875" android:color="#FF8CA6BB"/> + <item android:offset="0.472656" android:color="#FF8CA5B9"/> + <item android:offset="0.476562" android:color="#FF8DA4B8"/> + <item android:offset="0.480469" android:color="#FF8DA3B7"/> + <item android:offset="0.484375" android:color="#FF8EA2B6"/> + <item android:offset="0.488281" android:color="#FF8EA1B4"/> + <item android:offset="0.492187" android:color="#FF8FA0B3"/> + <item android:offset="0.496094" android:color="#FF8F9FB1"/> + <item android:offset="0.5" android:color="#FF8F9EB0"/> + <item android:offset="0.503906" android:color="#FF8F9DAF"/> + <item android:offset="0.507812" android:color="#FF909BAD"/> + <item android:offset="0.511719" android:color="#FF909AAC"/> + <item android:offset="0.515625" android:color="#FF9199AB"/> + <item android:offset="0.519531" android:color="#FF9198A9"/> + <item android:offset="0.523437" android:color="#FF9197A8"/> + <item android:offset="0.527344" android:color="#FF9196A6"/> + <item android:offset="0.53125" android:color="#FF9295A5"/> + <item android:offset="0.535156" android:color="#FF9294A3"/> + <item android:offset="0.539063" android:color="#FF9393A2"/> + <item android:offset="0.542969" android:color="#FF9392A1"/> + <item android:offset="0.546875" android:color="#FF9390A0"/> + <item android:offset="0.550781" android:color="#FF938F9E"/> + <item android:offset="0.554687" android:color="#FF948E9D"/> + <item android:offset="0.558594" android:color="#FF948D9B"/> + <item android:offset="0.5625" android:color="#FF958C9A"/> + <item android:offset="0.566406" android:color="#FF958B99"/> + <item android:offset="0.570312" android:color="#FF968A97"/> + <item android:offset="0.574219" android:color="#FF968996"/> + <item android:offset="0.578125" android:color="#FF968894"/> + <item android:offset="0.582031" android:color="#FF968793"/> + <item android:offset="0.585938" android:color="#FF978692"/> + <item android:offset="0.589844" android:color="#FF978490"/> + <item android:offset="0.59375" android:color="#FF98838F"/> + <item android:offset="0.597656" android:color="#FF98828D"/> + <item android:offset="0.601562" android:color="#FF98818C"/> + <item android:offset="0.605469" android:color="#FF98808B"/> + <item android:offset="0.609375" android:color="#FF997F8A"/> + <item android:offset="0.613281" android:color="#FF997E88"/> + <item android:offset="0.617187" android:color="#FF9A7D87"/> + <item android:offset="0.621094" android:color="#FF9A7C85"/> + <item android:offset="0.625" android:color="#FF9A7B84"/> + <item android:offset="0.628906" android:color="#FF9A7A82"/> + <item android:offset="0.632813" android:color="#FF9B7981"/> + <item android:offset="0.636719" android:color="#FF9B7780"/> + <item android:offset="0.640625" android:color="#FF9C767F"/> + <item android:offset="0.644531" android:color="#FF9C757D"/> + <item android:offset="0.648437" android:color="#FF9C747C"/> + <item android:offset="0.652344" android:color="#FF9D737A"/> + <item android:offset="0.65625" android:color="#FF9D7279"/> + <item android:offset="0.660156" android:color="#FF9D7178"/> + <item android:offset="0.664062" android:color="#FF9E7076"/> + <item android:offset="0.667969" android:color="#FF9E6F75"/> + <item android:offset="0.671875" android:color="#FF9F6E74"/> + <item android:offset="0.675781" android:color="#FF9F6C72"/> + <item android:offset="0.679688" android:color="#FF9F6B71"/> + <item android:offset="0.683594" android:color="#FF9F6A6F"/> + <item android:offset="0.6875" android:color="#FFA0696E"/> + <item android:offset="0.691406" android:color="#FFA0686C"/> + <item android:offset="0.695312" android:color="#FFA1676B"/> + <item android:offset="0.699219" android:color="#FFA1666A"/> + <item android:offset="0.703125" android:color="#FFA16569"/> + <item android:offset="0.707031" android:color="#FFA16467"/> + <item android:offset="0.710937" android:color="#FFA26366"/> + <item android:offset="0.714844" android:color="#FFA26264"/> + <item android:offset="0.71875" android:color="#FFA36163"/> + <item android:offset="0.722656" android:color="#FFA35F61"/> + <item android:offset="0.726563" android:color="#FFA45E60"/> + <item android:offset="0.730469" android:color="#FFA45D5F"/> + <item android:offset="0.734375" android:color="#FFA45C5D"/> + <item android:offset="0.738281" android:color="#FFA45B5C"/> + <item android:offset="0.742187" android:color="#FFA55A5B"/> + <item android:offset="0.746094" android:color="#FFA55959"/> + <item android:offset="0.75" android:color="#FFA65858"/> + <item android:offset="0.753906" android:color="#FFA65756"/> + <item android:offset="0.757812" android:color="#FFA65655"/> + <item android:offset="0.761719" android:color="#FFA65454"/> + <item android:offset="0.765625" android:color="#FFA75353"/> + <item android:offset="0.769531" android:color="#FFA75251"/> + <item android:offset="0.773437" android:color="#FFA85150"/> + <item android:offset="0.777344" android:color="#FFA8504E"/> + <item android:offset="0.78125" android:color="#FFA84F4D"/> + <item android:offset="0.785156" android:color="#FFA84E4B"/> + <item android:offset="0.789062" android:color="#FFA94D4A"/> + <item android:offset="0.792969" android:color="#FFA94C49"/> + <item android:offset="0.796875" android:color="#FFAA4B48"/> + <item android:offset="0.800781" android:color="#FFAA4A46"/> + <item android:offset="0.804687" android:color="#FFAB4845"/> + <item android:offset="0.808594" android:color="#FFAB4743"/> + <item android:offset="0.8125" android:color="#FFAB4642"/> + <item android:offset="0.816406" android:color="#FFAB4541"/> + <item android:offset="0.820312" android:color="#FFAC443F"/> + <item android:offset="0.824219" android:color="#FFAC433E"/> + <item android:offset="0.828125" android:color="#FFAD423C"/> + <item android:offset="0.832031" android:color="#FFAD413B"/> + <item android:offset="0.835937" android:color="#FFAD403A"/> + <item android:offset="0.839844" android:color="#FFAD3F38"/> + <item android:offset="0.84375" android:color="#FFAE3E37"/> + <item android:offset="0.847656" android:color="#FFAE3D35"/> + <item android:offset="0.851562" android:color="#FFAF3B34"/> + <item android:offset="0.855469" android:color="#FFAF3A33"/> + <item android:offset="0.859375" android:color="#FFAF3932"/> + <item android:offset="0.863281" android:color="#FFAF3830"/> + <item android:offset="0.867187" android:color="#FFB0372F"/> + <item android:offset="0.871094" android:color="#FFB0362D"/> + <item android:offset="0.875" android:color="#FFB1352C"/> + <item android:offset="0.878906" android:color="#FFB1342A"/> + <item android:offset="0.882812" android:color="#FFB13329"/> + <item android:offset="0.886719" android:color="#FFB23228"/> + <item android:offset="0.890625" android:color="#FFB23026"/> + <item android:offset="0.894531" android:color="#FFB22F25"/> + <item android:offset="0.898437" android:color="#FFB32E24"/> + <item android:offset="0.902344" android:color="#FFB32D22"/> + <item android:offset="0.90625" android:color="#FFB42C21"/> + <item android:offset="0.910156" android:color="#FFB42B1F"/> + <item android:offset="0.914062" android:color="#FFB42A1E"/> + <item android:offset="0.917969" android:color="#FFB4291D"/> + <item android:offset="0.921875" android:color="#FFB5281C"/> + <item android:offset="0.925781" android:color="#FFB5271A"/> + <item android:offset="0.929688" android:color="#FFB62619"/> + <item android:offset="0.933594" android:color="#FFB62517"/> + <item android:offset="0.9375" android:color="#FFB62316"/> + <item android:offset="0.941406" android:color="#FFB62214"/> + <item android:offset="0.945312" android:color="#FFB72113"/> + <item android:offset="0.949219" android:color="#FFB72012"/> + <item android:offset="0.953125" android:color="#FFB81F11"/> + <item android:offset="0.957031" android:color="#FFB81E0F"/> + <item android:offset="0.960938" android:color="#FFB91D0E"/> + <item android:offset="0.964844" android:color="#FFB91C0C"/> + <item android:offset="0.96875" android:color="#FFB91B0B"/> + <item android:offset="0.972656" android:color="#FFB91A09"/> + <item android:offset="0.976562" android:color="#FFBA1908"/> + <item android:offset="0.980469" android:color="#FFBA1807"/> + <item android:offset="0.984375" android:color="#FFBB1605"/> + <item android:offset="0.988281" android:color="#FFBB1504"/> + <item android:offset="0.992188" android:color="#FFBB1403"/> + <item android:offset="0.996094" android:color="#FFBB1301"/> + <item android:offset="1" android:color="#FFBC1200"/> + </gradient> + </aapt:attr> + </path> + <path + android:pathData="M47.107,114.704L92.527,106.393A1.999,2.001 126.099,0 1,94.855 108L95.882,113.586A1.999,2.001 126.099,0 1,94.277 115.913L48.857,124.225A1.999,2.001 126.099,0 1,46.528 122.618L45.501,117.031A1.999,2.001 126.099,0 1,47.107 114.704z" + android:fillColor="#007FAD"/> + <path + android:pathData="M49.101,127.74L94.521,119.429A1.999,2.001 126.099,0 1,96.85 121.036L97.877,126.622A1.999,2.001 126.099,0 1,96.271 128.949L50.851,137.261A1.999,2.001 126.099,0 1,48.522 135.654L47.495,130.067A1.999,2.001 126.099,0 1,49.101 127.74z" + android:fillColor="#007FAD"/> + <path + android:pathData="M39.442,64.918l42.77,-8.114l0.768,4.036l-42.77,8.114z" + android:fillColor="#C4C4C4"/> + <path + android:pathData="M40.849,72.312l37.019,-7.023l0.768,4.036l-37.019,7.023z" + android:fillColor="#C4C4C4"/> + <path + android:pathData="M42.256,79.706l32.98,-6.256l0.768,4.036l-32.98,6.256z" + android:fillColor="#C4C4C4"/> + </group> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_statistics_incidence.xml b/Corona-Warn-App/src/main/res/drawable/ic_statistics_incidence.xml new file mode 100644 index 0000000000000000000000000000000000000000..718990b832c5af742e11bb7d924187a9c47925fe --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_statistics_incidence.xml @@ -0,0 +1,357 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="106dp" + android:height="136dp" + android:viewportWidth="106" + android:viewportHeight="136"> + <path + android:fillColor="#F4F4F4" + android:pathData="M43.352,66.678C43.289,68.786 39.528,68.243 40.418,65.955C41.091,63.094 40.299,63.96 42.853,63.847C42.975,64.088 43.372,65.99 43.352,66.678Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M32.832,65.373C33.528,64.967 35.792,64.099 35.428,63.161L37.389,62.855C37.407,63.216 38.405,65.822 37.2,66.063C36.225,66.261 30.068,67.943 32.832,65.373Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M44.2,33.641C44.2,33.641 44.419,36.12 44.457,36.592C44.815,41.012 43.62,60.1 43.3,62.95C43.278,63.146 43.172,63.747 42.834,63.877C42.142,64.142 41.071,63.877 41.071,63.877C40.878,63.877 40.718,63.72 40.709,63.522C40.565,60.324 39.117,41.834 38.898,40.126C38.047,43.307 37.895,59.275 37.541,63.299C37.522,63.526 37.337,63.699 37.116,63.699C37.116,63.699 36.225,63.875 35.801,63.699C35.352,63.512 35.299,62.935 35.28,62.76C34.36,54.583 32.787,41.169 33.71,32.96L36.957,34.405L44.2,33.641Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M41.633,11.802C41.633,11.802 36.623,11.451 36.62,11.457L36.525,11.78C36.232,10.801 35.091,8.07 36.561,7.206C37.474,4.984 42.561,5.99 42.399,8.944C42.457,9.291 41.633,11.802 41.633,11.802Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M31.64,31.718L30.971,31.771C30.472,32.887 30.359,34.687 31.169,35.771C31.983,36.534 32.857,35.283 32.899,34.578C33.027,33.469 32.652,32.498 32.337,31.657L31.64,31.718Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M31.132,22.967C31.272,22.173 31.45,21.401 31.67,20.659C32.148,18.813 32.341,17.577 33.782,16.592C34.378,16.184 35.158,15.899 36.225,15.532C36.611,15.4 36.881,15.159 36.962,14.829C37.063,14.426 40.071,14.597 40.819,14.678C40.928,14.69 40.929,14.589 40.975,14.693C41.113,15.002 41.37,15.4 42.014,15.665C42.883,16.022 44.99,16.727 45.789,18.177C47.03,20.433 49.088,29.44 47.681,30.421C47.049,30.862 44.717,31.228 44.339,31.294C44.336,32.455 44.329,34.075 44.226,34.319C43.423,36.22 36.528,36.641 33.645,34.49C33.333,34.257 33.73,26.225 34.237,22.643C34.182,22.882 34.123,23.142 34.062,23.416C33.748,24.837 33.524,26.526 33.267,28.247C33.014,29.943 32.881,31.956 32.659,32.169C31.936,32.859 31.43,32.822 30.935,32.686C30.648,32.606 30.41,32.359 30.401,32.053C30.309,28.645 30.61,25.924 31.132,22.967Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M38.815,26.217H40.584C40.815,26.217 41.002,26.409 41.002,26.647V31.086C41.002,31.323 40.816,31.516 40.584,31.516H38.847C38.617,31.516 38.429,31.324 38.429,31.086V26.653C38.429,26.413 38.614,26.218 38.843,26.218" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M39.002,15.944C38.217,15.784 37.348,15.288 36.98,14.79C37.102,14.324 37.297,13.63 37.283,13.537C37.481,13.756 40.521,13.761 40.722,13.537C40.708,13.63 40.903,14.324 41.024,14.79C40.654,15.288 39.788,15.782 39.002,15.944Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M41.644,11.912V11.854C42.039,11.57 42.287,11.058 42.275,10.666C42.276,10.458 42.195,9.964 41.97,10.181C41.911,10.446 41.716,11.369 41.523,11.516C41.686,5.741 38.067,11.079 37.045,8.642C36.285,8.904 36.943,11.143 36.694,11.508C36.488,11.356 36.353,10.454 36.3,10.181C36.254,10.145 36.211,10.116 36.175,10.131C35.757,10.592 36.099,11.474 36.628,11.856C36.589,13.27 38.169,14.908 39.136,14.694C40.098,14.906 41.66,13.3 41.644,11.886" /> + <path + android:fillColor="#E4B489" + android:pathData="M41.499,11.31C41.5,11.255 41.502,11.205 41.502,11.153C41.503,11.21 41.503,11.269 41.503,11.328C41.502,11.322 41.5,11.316 41.499,11.31Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M42.802,30.058L42.808,29.42C41.818,28.838 40.164,28.577 39.095,29.282C38.322,30.017 39.406,30.977 40.055,31.076C41.071,31.294 41.998,31.008 42.803,30.768" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M20.766,20.17C20.766,20.17 25.102,21.906 29.358,19.665L28.435,16.18L22.033,16.251L20.766,20.17Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M23.646,18.726L23.531,21.948H26.507L26.068,18.726H23.646Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M27.508,16.597C27.254,21.552 22.301,20.665 22.422,15.688C22.675,10.734 27.63,11.62 27.508,16.597Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M21.135,35.607C19.048,43.938 21.051,55.908 21.697,65.516L23.212,65.565C23.212,65.565 24.081,49.941 24.449,46.388C24.561,45.309 24.927,43.002 25.113,41.998C25.128,41.922 25.289,41.59 25.309,41.977C25.431,44.428 26.536,47.123 26.782,49.818C27.355,56.117 27.229,60.501 27.478,65.7C27.478,65.7 29.234,66.736 29.237,66.725C29.604,65.274 30.587,53.325 30.587,51.534C30.587,51.166 30.71,43.08 29.114,35.729L21.135,35.607Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M31.569,36.354C30.647,40.626 36.835,41.452 32.354,35.471L31.569,36.354Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M32.817,35.264C32.587,33.646 32.184,31.174 31.815,29.481C31.201,26.663 30.465,22.375 26.413,21.273C25.323,21.998 24.34,21.403 23.58,21.265C21.105,20.817 18.898,24.92 18.311,27.031C17.697,29.236 16.645,32.523 17.119,33.279C18.04,34.749 18.841,33.963 21.048,32.421C21.153,32.348 21.26,32.271 21.368,32.194C21.333,33.675 21.265,35.008 21.136,35.607C20.839,36.992 29.484,36.954 29.238,35.974C28.718,33.899 28.501,30.094 28.501,27.153C28.501,26.908 29.114,28.154 29.361,28.991C29.975,31.074 30.501,34.634 31.316,36.403C31.571,36.954 31.931,36.725 32.528,36.519C33.039,36.337 32.846,35.476 32.817,35.264ZM20.03,30.584C20.03,30.584 20.153,30.461 20.644,28.746C20.932,27.739 21.38,26.173 21.38,26.173C21.38,26.173 21.41,28.031 21.396,30.125C20.468,30.406 20.03,30.584 20.03,30.584Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M23.258,64.844L21.627,64.642C21.58,65.061 21.013,65.745 21.326,67.371C21.351,67.501 21.381,68.737 21.423,68.87C21.461,68.994 21.995,69.174 21.995,69.052C21.995,68.928 21.843,67.401 22.118,67.949C22.241,68.194 22.241,69.052 22.854,69.542C23.556,70.102 25.764,70.282 24.941,69.297C23.714,67.826 23.343,65.49 23.247,64.976" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M27.447,64.859C27.447,64.859 27.434,65.718 27.274,66.359C27.151,66.849 27.028,68.319 27.519,69.544H28.256C28.256,69.544 28.01,68.197 28.133,67.461C28.166,67.265 28.256,68.319 28.993,69.177C29.331,69.571 29.636,69.625 29.779,69.641C30.043,69.67 31.61,69.667 31.693,69.667C31.939,69.667 32.184,69.177 31.199,68.742C30.935,68.625 30.606,68.402 30.343,68.074C29.896,67.517 29.709,66.559 29.464,65.211L27.447,64.859Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M25.383,25.725L27.059,25.758C27.279,25.763 27.438,25.944 27.415,26.162L26.979,30.206C26.956,30.426 26.758,30.6 26.538,30.595L24.863,30.562C24.643,30.557 24.483,30.375 24.507,30.157L24.943,26.113C24.966,25.894 25.162,25.72 25.383,25.725Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M23.094,30.226L23.445,30.742C25.678,30.726 28.031,27.663 25.085,28.003C24.91,28.024 24.739,28.079 24.585,28.166C23.859,28.574 23.308,29.214 22.778,29.689" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M25.922,13.554C25.186,13.309 23.712,13.432 23.207,17.954C23.137,18.583 23.59,19.19 23.431,19.535C22.96,20.562 22.34,21.035 21.419,20.769C18.925,20.047 20.901,18.956 21.023,16.498C21.257,11.717 24.605,10.99 26.008,11.759C26.008,11.759 28.665,10.975 29.024,16.583C29.024,16.583 29.21,19.479 29.612,19.992C30.014,20.506 28.868,20.901 27.924,20.531C26.98,20.161 27.274,18.063 27.274,18.063C27.274,18.063 28.064,14.267 25.922,13.554Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M13.352,68.678C13.289,70.786 9.528,70.243 10.418,67.955C11.091,65.094 10.299,65.96 12.853,65.847C12.975,66.088 13.371,67.99 13.352,68.678Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M2.832,67.373C3.528,66.967 5.792,66.099 5.428,65.161L7.389,64.855C7.407,65.216 8.405,67.822 7.2,68.063C6.225,68.261 0.068,69.943 2.832,67.373Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M14.2,35.641C14.2,35.641 14.418,38.12 14.457,38.592C14.815,43.012 13.62,62.1 13.3,64.95C13.278,65.146 13.172,65.747 12.834,65.877C12.142,66.142 11.07,65.877 11.07,65.877C10.877,65.877 10.718,65.72 10.709,65.522C10.565,62.324 9.117,43.834 8.898,42.126C8.047,45.307 7.895,61.275 7.541,65.299C7.522,65.526 7.337,65.699 7.115,65.699C7.115,65.699 6.225,65.875 5.801,65.699C5.352,65.512 5.299,64.935 5.28,64.76C4.36,56.583 2.787,43.169 3.709,34.96L6.957,36.405L14.2,35.641Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M11.632,13.802C11.632,13.802 6.623,13.451 6.62,13.457L6.525,13.78C6.232,12.801 5.091,10.07 6.561,9.206C7.474,6.984 12.561,7.99 12.399,10.944C12.457,11.291 11.632,13.802 11.632,13.802Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M1.64,33.718L0.971,33.771C0.472,34.887 0.359,36.687 1.169,37.771C1.983,38.534 2.857,37.283 2.899,36.578C3.026,35.469 2.652,34.498 2.337,33.657L1.64,33.718Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M1.132,24.967C1.272,24.173 1.449,23.401 1.669,22.659C2.148,20.813 2.341,19.577 3.781,18.592C4.378,18.184 5.158,17.899 6.225,17.532C6.611,17.4 6.881,17.159 6.962,16.829C7.063,16.426 10.071,16.597 10.818,16.678C10.928,16.69 10.929,16.589 10.975,16.692C11.113,17.002 11.37,17.4 12.013,17.665C12.883,18.022 14.99,18.727 15.788,20.177C17.029,22.433 19.087,31.44 17.68,32.421C17.049,32.862 14.717,33.228 14.339,33.294C14.336,34.455 14.328,36.075 14.226,36.319C13.423,38.22 6.527,38.641 3.645,36.49C3.332,36.257 3.73,28.225 4.237,24.643C4.181,24.882 4.122,25.142 4.062,25.416C3.748,26.837 3.524,28.526 3.267,30.247C3.013,31.943 2.881,33.956 2.658,34.169C1.936,34.859 1.43,34.822 0.935,34.686C0.648,34.606 0.41,34.359 0.401,34.053C0.309,30.645 0.61,27.924 1.132,24.967Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M8.814,28.217H10.584C10.814,28.217 11.002,28.409 11.002,28.647V33.086C11.002,33.323 10.816,33.516 10.584,33.516H8.847C8.616,33.516 8.429,33.324 8.429,33.086V28.653C8.429,28.413 8.614,28.218 8.843,28.218" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M9.002,17.944C8.216,17.784 7.348,17.288 6.98,16.79C7.101,16.324 7.297,15.63 7.283,15.537C7.481,15.756 10.521,15.761 10.722,15.537C10.708,15.63 10.903,16.324 11.024,16.79C10.654,17.288 9.788,17.782 9.002,17.944Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M11.644,13.912V13.854C12.039,13.57 12.287,13.058 12.274,12.666C12.276,12.458 12.195,11.964 11.969,12.181C11.91,12.446 11.716,13.369 11.523,13.516C11.685,7.741 8.067,13.079 7.045,10.642C6.284,10.904 6.943,13.143 6.693,13.508C6.488,13.356 6.353,12.454 6.3,12.181C6.253,12.145 6.211,12.116 6.175,12.131C5.757,12.592 6.099,13.474 6.628,13.856C6.589,15.27 8.169,16.908 9.136,16.694C10.098,16.906 11.66,15.3 11.644,13.886" /> + <path + android:fillColor="#E4B489" + android:pathData="M11.499,13.31C11.5,13.255 11.502,13.205 11.502,13.153C11.503,13.21 11.503,13.269 11.503,13.328C11.502,13.322 11.5,13.316 11.499,13.31Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M12.802,32.058L12.808,31.42C11.818,30.838 10.164,30.577 9.095,31.282C8.322,32.017 9.406,32.977 10.054,33.076C11.07,33.294 11.998,33.008 12.803,32.768" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M65.872,20.458L65.56,23.689L64.925,24.21L64.291,24.601L65.179,26.556L70.13,26.686L69.495,24.601L69.622,23.559L69.235,23.384C68.924,23.243 68.705,22.948 68.66,22.601L68.377,20.457H65.872V20.458Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M69.866,18.195C69.603,23.465 64.481,22.521 64.607,17.228C64.868,11.957 69.991,12.9 69.866,18.195Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M63.275,38.415C61.117,47.277 63.189,60.011 63.857,70.23L65.423,70.282C65.423,70.282 66.322,53.663 66.702,49.883C66.818,48.735 67.196,46.281 67.389,45.214C67.404,45.133 67.571,44.78 67.591,45.192C67.718,47.798 68.86,50.665 69.114,53.532C69.707,60.232 69.576,64.896 69.834,70.426C69.834,70.426 71.65,71.528 71.653,71.517C72.032,69.974 73.049,57.262 73.049,55.357C73.049,54.966 73.544,46.825 71.526,38.545L63.275,38.415Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M73.63,39.011C72.438,43.495 78.781,44.728 74.491,38.118L73.63,39.011Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M59.123,35.939C60.075,37.503 60.904,36.666 63.185,35.027C63.294,34.949 63.403,34.867 63.516,34.784C63.481,36.359 63.41,37.778 63.276,38.415C62.969,39.889 71.908,39.849 71.654,38.806C71.116,36.598 70.892,32.551 70.892,29.423C70.892,29.162 71.316,30.476 71.527,31.378C72.063,33.662 72.452,36.989 73.295,38.871C73.558,39.458 73.931,39.214 74.548,38.995C75.076,38.801 74.863,37.886 74.846,37.658C74.727,36.008 74.446,33.699 74.064,31.898C73.478,29.126 73.036,25.016 69.62,23.486C69.565,23.461 69.5,23.504 69.501,23.568C69.534,24.737 68.327,26.424 67.718,26.424C66.575,26.424 64.671,24.73 64.407,23.362C61.69,24.176 60.882,27.346 60.356,29.291C59.721,31.639 58.633,35.135 59.123,35.939Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M65.467,69.501L63.783,69.302C63.734,69.748 63.148,70.475 63.472,72.205C63.497,72.343 63.529,73.658 63.572,73.8C63.611,73.931 64.163,74.123 64.163,73.993C64.163,73.861 64.006,72.237 64.29,72.82C64.417,73.08 64.417,73.993 65.052,74.514C65.777,75.109 68.06,75.301 67.21,74.253C65.94,72.689 65.561,70.21 65.462,69.662" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M69.815,69.585C69.815,69.585 69.788,70.445 69.622,71.126C69.495,71.647 69.368,73.211 69.876,74.514H70.637C70.637,74.514 70.383,73.08 70.51,72.298C70.545,72.09 70.637,73.211 71.399,74.123C71.749,74.543 72.064,74.6 72.212,74.617C72.485,74.648 74.105,74.644 74.191,74.644C74.445,74.644 74.699,74.123 73.68,73.66C73.407,73.536 73.067,73.299 72.795,72.95C72.333,72.357 72.121,71.354 71.867,69.92L69.815,69.585Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M67.668,27.903L69.401,27.939C69.628,27.944 69.793,28.137 69.769,28.369L69.318,32.671C69.294,32.904 69.09,33.089 68.863,33.084L67.13,33.048C66.903,33.043 66.738,32.85 66.762,32.618L67.213,28.316C67.237,28.083 67.44,27.898 67.668,27.903Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M65.301,32.691L65.664,33.24C68.036,33.223 70.537,29.788 67.101,30.364C66.217,30.781 65.583,31.558 64.975,32.119" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M70.783,24.183C70.604,24.043 70.457,23.942 70.256,23.82C70.024,23.68 69.86,23.603 69.604,23.492C69.584,23.483 69.537,23.492 69.523,23.519C69.307,23.465 69.136,23.366 68.957,23.195C68.58,22.835 68.339,22.197 68.465,21.545C68.534,21.193 68.666,21.047 68.738,20.957C70.369,18.939 69.296,15.726 69.296,15.726C69.296,15.726 68.379,17.71 64.922,19.068C64.922,19.068 65.065,19.99 65.573,20.642C65.609,20.69 65.99,21.043 66.004,21.597C66.017,22.141 66.018,22.691 65.656,23.523C65.216,24.54 64.095,24.785 63.245,24.436C62.61,24.175 62.356,23.523 62.327,22.881C62.327,22.881 64.057,22.985 63.65,21.002C63.345,19.511 62.982,16.254 64.415,14.371C64.435,14.345 64.456,14.319 64.476,14.293C64.49,14.273 64.506,14.254 64.523,14.235C64.552,14.2 64.583,14.165 64.613,14.131C64.642,14.1 64.671,14.069 64.701,14.037C65.15,13.569 65.741,13.218 66.515,13.045C66.515,13.045 69.464,12.628 69.972,14.403C69.972,14.403 70.786,14.822 70.888,16.388C70.99,17.955 70.735,18.528 70.684,19.73C70.623,21.191 72.5,22 71.89,22.881C71.39,23.5 72.164,23.762 70.783,24.183Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M95.872,17.458L95.56,20.689L94.925,21.21L94.29,21.601L95.179,23.556L100.129,23.686L99.495,21.601L99.622,20.559L99.234,20.384C98.923,20.243 98.705,19.948 98.659,19.601L98.376,17.457H95.872V17.458Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M99.865,15.195C99.603,20.465 94.481,19.521 94.606,14.228C94.868,8.957 99.991,9.9 99.865,15.195Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M93.275,35.415C91.117,44.277 93.189,57.011 93.856,67.23L95.423,67.282C95.423,67.282 96.321,50.663 96.702,46.883C96.818,45.735 97.196,43.281 97.389,42.214C97.404,42.133 97.57,41.78 97.591,42.192C97.718,44.798 98.86,47.665 99.114,50.532C99.707,57.232 99.576,61.896 99.834,67.426C99.834,67.426 101.65,68.528 101.652,68.517C102.032,66.974 103.049,54.262 103.049,52.357C103.049,51.966 103.544,43.825 101.526,35.545L93.275,35.415Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M103.63,36.011C102.438,40.495 108.781,41.728 104.491,35.118L103.63,36.011Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M90.5,31.5C92.344,34.53 89.5,35 93.276,35.415C92.969,36.889 101.907,36.849 101.654,35.806C101.115,33.598 100.892,29.551 100.892,26.423C100.892,26.162 101.316,27.476 101.527,28.378C102.062,30.662 102.452,33.989 103.295,35.871C103.558,36.458 103.931,36.214 104.548,35.995C105.076,35.801 104.862,34.886 104.846,34.658C104.727,33.008 104.446,30.699 104.064,28.898C103.478,26.126 103.036,22.016 99.62,20.486C99.564,20.461 99.499,20.504 99.501,20.568C99.534,21.737 98.327,23.424 97.717,23.424C96.575,23.424 94.671,21.73 94.407,20.362C91.689,21.176 91.5,23.424 91.5,26.5C90.865,28.847 90.01,30.696 90.5,31.5Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M95.467,66.501L93.782,66.302C93.734,66.748 93.148,67.475 93.471,69.205C93.497,69.343 93.529,70.658 93.572,70.8C93.611,70.931 94.163,71.123 94.163,70.993C94.163,70.861 94.006,69.237 94.29,69.82C94.417,70.08 94.417,70.993 95.052,71.514C95.776,72.109 98.06,72.301 97.21,71.253C95.94,69.689 95.561,67.21 95.462,66.662" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M99.814,66.585C99.814,66.585 99.788,67.445 99.621,68.126C99.494,68.647 99.368,70.211 99.875,71.514H100.637C100.637,71.514 100.383,70.08 100.51,69.298C100.544,69.09 100.637,70.211 101.398,71.123C101.749,71.543 102.064,71.6 102.212,71.617C102.485,71.648 104.105,71.644 104.191,71.644C104.445,71.644 104.699,71.123 103.679,70.66C103.407,70.536 103.066,70.299 102.795,69.95C102.333,69.357 102.121,68.354 101.867,66.92L99.814,66.585Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M97.668,24.903L99.401,24.939C99.628,24.944 99.793,25.137 99.769,25.369L99.318,29.67C99.294,29.904 99.09,30.089 98.862,30.084L97.13,30.048C96.902,30.043 96.738,29.85 96.762,29.618L97.212,25.316C97.236,25.083 97.439,24.898 97.668,24.903Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M95.301,29.691L95.664,30.24C98.036,30.223 100.537,26.788 97.101,27.364C96.217,27.781 95.582,28.558 94.974,29.119" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M100.783,21.183C100.604,21.043 100.457,20.942 100.256,20.82C100.024,20.68 99.86,20.603 99.604,20.492C99.583,20.483 99.536,20.492 99.522,20.519C99.307,20.465 99.135,20.366 98.956,20.195C98.579,19.835 98.338,19.197 98.465,18.545C98.534,18.193 98.666,18.047 98.738,17.957C100.369,15.939 99.295,12.726 99.295,12.726C99.295,12.726 98.379,14.71 94.921,16.068C94.921,16.068 95.065,16.99 95.572,17.642C95.609,17.69 95.99,18.043 96.004,18.597C96.017,19.141 96.018,19.691 95.656,20.523C95.216,21.54 94.095,21.785 93.244,21.436C92.61,21.175 92.356,20.523 92.327,19.881C92.327,19.881 94.057,19.985 93.649,18.002C93.345,16.511 92.982,13.254 94.415,11.371C94.435,11.345 94.455,11.319 94.476,11.293C94.49,11.273 94.506,11.254 94.523,11.235C94.552,11.2 94.582,11.165 94.613,11.131C94.642,11.1 94.671,11.069 94.7,11.037C95.15,10.569 95.741,10.218 96.514,10.045C96.514,10.045 99.464,9.629 99.972,11.403C99.972,11.403 100.785,11.822 100.888,13.388C100.99,14.955 100.735,15.528 100.684,16.73C100.623,18.191 100.582,19.654 102.108,19.967C102.111,19.964 102.164,20.762 100.783,21.183Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M85.954,69.261C85.888,71.409 81.936,70.855 82.871,68.524C83.578,65.61 82.745,66.492 85.43,66.377C85.558,66.623 85.975,68.56 85.954,69.261Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M74.9,67.932C75.631,67.518 78.01,66.634 77.627,65.679L79.688,65.367C79.707,65.734 80.756,68.389 79.49,68.635C78.465,68.836 71.995,70.549 74.9,67.932Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M86.845,35.607C86.845,35.607 87.075,38.133 87.116,38.613C87.491,43.116 86.236,62.561 85.899,65.463C85.876,65.662 85.765,66.275 85.41,66.407C84.683,66.677 83.557,66.407 83.557,66.407C83.354,66.407 83.186,66.248 83.177,66.046C83.025,62.789 81.504,43.954 81.274,42.213C80.379,45.454 80.22,61.72 79.848,65.819C79.828,66.05 79.633,66.226 79.4,66.226C79.4,66.226 78.465,66.406 78.019,66.226C77.548,66.036 77.492,65.448 77.472,65.27C76.505,56.94 74.852,43.276 75.822,34.914L79.234,36.386L86.845,35.607Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M84.147,13.362C84.147,13.362 78.883,13.004 78.88,13.009L78.78,13.339C78.472,12.342 77.273,9.56 78.818,8.68C79.778,6.416 85.123,7.441 84.953,10.45C85.014,10.804 84.147,13.362 84.147,13.362Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M73.647,33.648L72.944,33.702C72.42,34.84 72.301,36.674 73.152,37.777C74.008,38.554 74.925,37.281 74.97,36.563C75.104,35.432 74.711,34.443 74.379,33.586L73.647,33.648Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M73.113,24.734C73.26,23.926 73.447,23.139 73.678,22.384C74.181,20.503 74.383,19.244 75.897,18.24C76.524,17.825 77.343,17.535 78.465,17.161C78.871,17.026 79.155,16.781 79.24,16.444C79.345,16.034 82.507,16.208 83.292,16.291C83.407,16.303 83.408,16.2 83.457,16.306C83.601,16.621 83.872,17.026 84.547,17.296C85.461,17.66 87.675,18.378 88.514,19.855C89.819,22.153 91.981,31.328 90.503,32.327C89.839,32.777 87.388,33.149 86.991,33.217C86.988,34.4 86.98,36.05 86.872,36.298C85.234,37.382 80.017,37.657 75.754,37.021C73.976,37.021 74.717,36.298 74.717,34.108C74.717,34.911 73.426,34.774 72.906,34.635C72.605,34.554 72.355,34.301 72.345,33.99C72.248,30.518 72.564,27.747 73.113,24.734ZM87.086,30.279C87.521,30.113 87.712,30.192 87.926,30.113C87.957,29.103 86.967,24.947 86.845,24.582C86.845,24.599 87.009,27.337 87.086,30.279Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M81.186,28.045H83.046C83.288,28.045 83.485,28.241 83.485,28.484V33.005C83.485,33.246 83.289,33.444 83.046,33.444H81.22C80.978,33.444 80.78,33.248 80.78,33.005V28.489C80.78,28.245 80.975,28.047 81.216,28.047" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M81.383,17.581C80.557,17.418 79.645,16.913 79.258,16.406C79.385,15.931 79.591,15.224 79.576,15.13C79.784,15.352 82.979,15.358 83.19,15.13C83.175,15.224 83.381,15.931 83.508,16.406C83.119,16.913 82.209,17.416 81.383,17.581Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M84.16,13.474V13.415C84.574,13.126 84.835,12.604 84.822,12.204C84.823,11.992 84.738,11.489 84.502,11.71C84.439,11.98 84.235,12.921 84.033,13.07C84.203,7.188 80.401,12.625 79.326,10.143C78.527,10.41 79.219,12.69 78.957,13.062C78.741,12.907 78.599,11.988 78.544,11.71C78.495,11.674 78.45,11.644 78.412,11.659C77.973,12.129 78.333,13.027 78.888,13.417C78.848,14.857 80.507,16.526 81.524,16.307C82.535,16.523 84.176,14.888 84.16,13.447" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M84.007,12.86C84.008,12.804 84.009,12.753 84.009,12.701C84.011,12.759 84.011,12.818 84.011,12.879C84.009,12.872 84.008,12.866 84.007,12.86Z" /> + <path + android:fillColor="#F4F4F4" + android:pathData="M85.376,31.958L85.383,31.307C84.342,30.715 82.604,30.449 81.481,31.167C80.668,31.916 81.808,32.894 82.489,32.995C83.557,33.216 84.531,32.925 85.377,32.681" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M71.09,33.963L70.932,38.365H75.002L74.402,33.963H71.09Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M76.372,31.057C76.024,37.824 69.25,36.613 69.416,29.815C69.762,23.048 76.538,24.258 76.372,31.057Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M67.655,57.022C64.801,68.401 67.541,84.752 68.424,97.875L70.496,97.942C70.496,97.942 71.684,76.601 72.188,71.748C72.341,70.273 72.841,66.453 73.096,65.082C73.116,64.979 73.336,64.525 73.363,65.054C73.531,68.401 75.042,72.752 75.378,76.433C76.162,85.036 75.989,91.026 76.33,98.126C76.33,98.126 78.732,99.542 78.736,99.527C79.238,97.545 80.583,81.223 80.583,78.776C80.583,78.274 80.751,67.229 78.568,57.189L67.655,57.022Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M81.926,58.043C80.665,63.878 89.127,65.006 82.999,56.836L81.926,58.043Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M83.632,56.553C83.318,54.344 82.767,50.967 82.261,48.655C81.422,44.806 80.415,38.949 74.874,37.443C73.944,38.338 71.748,37.75 70.999,37.443C70.986,37.438 70.981,37.443 70.966,37.443C65.85,37.493 64.597,42.423 63.793,45.308C62.954,48.32 61.515,52.81 62.163,53.842C63.422,55.85 64.519,54.776 67.535,52.671C67.68,52.57 67.826,52.465 67.974,52.36C67.927,54.381 67.833,56.203 67.656,57.022C67.25,58.914 79.073,58.862 78.737,57.524C78.026,54.689 77.73,49.491 77.73,45.475C77.73,45.14 78.568,46.842 78.905,47.985C79.745,50.83 80.465,55.693 81.58,58.109C81.927,58.862 82.421,58.55 83.237,58.268C83.935,58.019 83.672,56.843 83.632,56.553ZM66.144,50.161C66.144,50.161 66.312,49.993 66.983,47.651C67.378,46.275 67.991,44.136 67.991,44.136C67.991,44.136 68.031,46.673 68.012,49.535C66.743,49.918 66.144,50.161 66.144,50.161Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M70.489,97.098L68.326,96.682C68.262,97.254 67.487,98.188 67.915,100.408C67.949,100.586 67.991,102.274 68.048,102.457C68.1,102.626 68.83,102.872 68.83,102.704C68.83,102.535 68.622,100.45 68.998,101.198C69.166,101.533 69.166,102.704 70.005,103.374C70.964,104.138 73.984,104.384 72.859,103.039C71.18,101.031 70.689,97.759 70.558,97.057" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M68.469,22.33C68.469,22.33 69.206,20.688 70.533,21.073C70.533,21.073 71.016,18.496 74.303,19.088C77.591,19.681 77.535,21.925 77.535,21.925C77.535,21.925 81.043,21.93 80.113,26.055C80.113,26.055 80.944,27.923 79.617,28.873C79.617,28.873 80.082,30.925 78.032,31.157C78.032,31.157 77.871,31.76 77.361,31.656C77.361,31.656 76.456,32.811 76.127,32.031C75.93,31.564 75.974,30.64 76.041,29.974C76.1,29.387 75.925,28.803 75.551,28.346C75.094,27.79 74.409,27.156 73.625,27.103C72.205,27.005 70.249,27.998 69.98,29.025C69.779,29.793 70.35,32.267 69.131,32.273C68.353,32.278 67.979,31.144 67.979,31.144C67.979,31.144 65.719,30.761 66.354,28.975C66.354,28.975 65.214,27.671 66.165,26.12C66.165,26.122 65.076,22.383 68.469,22.33Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M76.298,97.04C76.298,97.04 76.269,98.151 76.051,99.025C75.883,99.694 75.715,101.702 76.387,103.376H77.394C77.394,103.376 77.059,101.535 77.226,100.531C77.272,100.263 77.394,101.702 78.402,102.874C78.865,103.412 79.281,103.486 79.478,103.508C79.839,103.548 81.981,103.543 82.095,103.543C82.431,103.543 82.767,102.874 81.419,102.28C81.058,102.121 80.608,101.816 80.248,101.368C79.637,100.606 79.387,99.316 79.051,97.475L76.298,97.04Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M73.465,43.524L75.757,43.569C76.058,43.576 76.276,43.823 76.244,44.121L75.648,49.645C75.616,49.945 75.346,50.182 75.045,50.176L72.754,50.13C72.453,50.124 72.235,49.876 72.267,49.578L72.863,44.054C72.895,43.755 73.163,43.517 73.465,43.524Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M70.334,49.672L70.814,50.376C73.951,50.355 77.26,45.944 72.715,46.683C71.547,47.219 70.707,48.216 69.903,48.937" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M15.298,33.864L14.875,38.137L14.017,38.826L13.158,39.343L14.36,41.929L21.058,42.101L20.199,39.343L20.371,37.965L19.847,37.734C19.426,37.548 19.131,37.156 19.069,36.698L18.686,33.862H15.298V33.864Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M20.701,30.87C20.345,37.84 13.416,36.592 13.586,29.591C13.939,22.621 20.871,23.867 20.701,30.87Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M11.784,57.614C8.865,69.334 11.667,86.175 12.571,99.692L14.69,99.761C14.69,99.761 15.906,77.78 16.421,72.781C16.577,71.263 17.089,68.017 17.35,66.606C17.371,66.499 17.596,66.032 17.623,66.576C17.795,70.023 19.34,73.815 19.684,77.607C20.486,86.468 20.309,92.637 20.658,99.95C20.658,99.95 23.115,101.408 23.118,101.393C23.632,99.352 25.007,82.54 25.007,80.02C25.007,79.503 25.677,68.736 22.947,57.786L11.784,57.614Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M25.794,58.401C24.181,64.332 32.763,65.963 26.958,57.221L25.794,58.401Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M6.167,54.339C7.455,56.407 8.576,55.3 11.662,53.132C11.81,53.029 11.957,52.92 12.11,52.812C12.062,54.894 11.966,56.771 11.786,57.613C11.37,59.563 23.463,59.509 23.12,58.131C22.392,55.211 22.09,49.857 22.09,45.721C22.09,45.376 22.663,47.113 22.948,48.306C23.673,51.327 24.2,55.728 25.34,58.217C25.696,58.992 26.201,58.67 27.035,58.381C27.75,58.124 27.461,56.914 27.439,56.612C27.278,54.43 26.898,51.376 26.381,48.994C25.588,45.328 24.99,39.891 20.369,37.868C20.293,37.835 20.206,37.892 20.207,37.977C20.252,39.523 18.619,41.755 17.795,41.755C16.249,41.755 13.673,39.514 13.316,37.704C9.639,38.782 8.547,42.973 7.834,45.547C6.975,48.651 5.504,53.275 6.167,54.339ZM10.359,50.295C10.479,49.997 10.702,49.351 11.099,47.961L11.915,45.098C11.95,44.971 12.138,44.995 12.139,45.128C12.15,46.121 12.165,47.937 12.151,49.902C11.343,50.147 10.798,50.336 10.505,50.445C10.41,50.481 10.321,50.388 10.359,50.295Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M14.75,98.727L12.471,98.465C12.405,99.054 11.612,100.016 12.05,102.303C12.084,102.486 12.127,104.225 12.186,104.413C12.239,104.587 12.986,104.84 12.986,104.668C12.986,104.494 12.773,102.346 13.158,103.116C13.329,103.461 13.329,104.668 14.188,105.357C15.169,106.145 18.258,106.398 17.107,105.012C15.39,102.944 14.877,99.664 14.743,98.94" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M20.632,98.838C20.632,98.838 20.596,99.976 20.371,100.876C20.199,101.565 20.027,103.633 20.714,105.357H21.744C21.744,105.357 21.401,103.461 21.573,102.427C21.619,102.151 21.744,103.633 22.775,104.84C23.249,105.395 23.675,105.471 23.876,105.493C24.245,105.534 26.436,105.529 26.553,105.529C26.896,105.529 27.24,104.84 25.861,104.228C25.492,104.064 25.031,103.751 24.664,103.289C24.039,102.504 23.752,101.177 23.409,99.281L20.632,98.838Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M17.728,43.711L20.072,43.757C20.379,43.764 20.603,44.019 20.57,44.326L19.96,50.016C19.927,50.324 19.651,50.569 19.344,50.562L17,50.516C16.692,50.509 16.469,50.254 16.501,49.947L17.111,44.257C17.144,43.949 17.419,43.704 17.728,43.711Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M14.525,50.043L15.016,50.769C18.226,50.746 21.609,46.203 16.96,46.965C15.765,47.516 14.906,48.544 14.083,49.287" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M21.942,38.79C21.7,38.606 21.501,38.471 21.229,38.311C20.915,38.125 20.694,38.023 20.347,37.877C20.319,37.864 20.256,37.877 20.237,37.913C19.945,37.84 19.713,37.709 19.471,37.484C18.961,37.008 18.635,36.163 18.806,35.302C18.899,34.836 19.077,34.643 19.175,34.524C21.382,31.854 19.929,27.606 19.929,27.606C19.929,27.606 18.689,30.229 14.012,32.025C14.012,32.025 14.205,33.245 14.892,34.107C14.942,34.171 15.457,34.638 15.476,35.37C15.493,36.089 15.495,36.817 15.006,37.918C14.41,39.262 12.894,39.586 11.743,39.125C10.884,38.78 10.541,37.918 10.501,37.068C10.501,37.068 12.842,37.206 12.291,34.583C11.879,32.611 11.387,28.304 13.326,25.813C13.354,25.779 13.381,25.744 13.409,25.71C13.428,25.684 13.45,25.658 13.472,25.634C13.512,25.587 13.553,25.541 13.594,25.496C13.634,25.455 13.673,25.413 13.713,25.372C14.321,24.753 15.121,24.288 16.167,24.06C16.167,24.06 20.158,23.509 20.845,25.856C20.845,25.856 21.945,26.41 22.085,28.481C22.222,30.553 21.877,31.311 21.808,32.901C21.726,34.833 21.671,36.768 23.735,37.182C23.738,37.179 23.81,38.233 21.942,38.79Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M95.222,103.977C95.133,106.863 89.824,106.12 91.081,102.988C92.03,99.073 90.912,100.258 94.518,100.104C94.69,100.434 95.249,103.037 95.222,103.977Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M80.372,102.192C81.355,101.636 84.55,100.449 84.036,99.166L86.805,98.747C86.83,99.24 88.239,102.807 86.538,103.137C85.162,103.407 76.471,105.708 80.372,102.192Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M96.419,58.771C96.419,58.771 96.727,62.164 96.782,62.809C97.286,68.857 95.6,94.977 95.148,98.876C95.117,99.144 94.968,99.967 94.49,100.144C93.513,100.507 92.001,100.144 92.001,100.144C91.729,100.144 91.504,99.93 91.491,99.659C91.288,95.284 89.243,69.983 88.935,67.645C87.733,71.998 87.519,93.848 87.019,99.354C86.992,99.664 86.73,99.901 86.418,99.901C86.418,99.901 85.162,100.142 84.563,99.901C83.929,99.646 83.855,98.856 83.827,98.616C82.529,87.427 80.309,69.073 81.611,57.84L86.195,59.817L96.419,58.771Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M92.795,28.889C92.795,28.889 85.723,28.409 85.719,28.416L85.585,28.858C85.171,27.519 83.561,23.782 85.636,22.6C86.925,19.559 94.105,20.936 93.877,24.978C93.959,25.453 92.795,28.889 92.795,28.889Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M78.689,56.14L77.745,56.212C77.041,57.74 76.881,60.203 78.025,61.686C79.174,62.73 80.407,61.019 80.467,60.055C80.647,58.536 80.118,57.207 79.673,56.056L78.689,56.14Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M77.972,44.165C78.17,43.08 78.421,42.023 78.731,41.008C79.407,38.482 79.679,36.791 81.712,35.443C82.555,34.884 83.655,34.495 85.162,33.993C85.707,33.811 86.088,33.481 86.202,33.03C86.344,32.479 90.59,32.713 91.645,32.824C91.8,32.84 91.801,32.702 91.867,32.843C92.061,33.268 92.424,33.811 93.332,34.174C94.559,34.663 97.533,35.627 98.661,37.612C100.413,40.698 103.318,53.022 101.332,54.365C100.44,54.969 97.148,55.469 96.615,55.56C96.611,57.149 96.6,59.366 96.455,59.699C94.255,61.156 87.246,61.525 81.52,60.671C79.131,60.671 80.127,59.699 80.127,56.758C80.127,57.836 78.393,57.651 77.694,57.465C77.29,57.356 76.954,57.017 76.941,56.598C76.81,51.935 77.235,48.212 77.972,44.165ZM96.742,51.614C97.326,51.391 97.582,51.496 97.871,51.391C97.913,50.034 96.582,44.452 96.419,43.961C96.419,43.984 96.638,47.661 96.742,51.614Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M88.817,48.613H91.315C91.64,48.613 91.905,48.876 91.905,49.202V55.275C91.905,55.6 91.642,55.864 91.315,55.864H88.862C88.537,55.864 88.272,55.602 88.272,55.275V49.209C88.272,48.881 88.534,48.615 88.857,48.615" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M89.082,34.556C87.972,34.337 86.747,33.659 86.228,32.978C86.398,32.34 86.674,31.39 86.654,31.263C86.934,31.563 91.226,31.57 91.509,31.263C91.489,31.39 91.765,32.34 91.936,32.978C91.413,33.659 90.191,34.335 89.082,34.556Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M92.811,29.04V28.96C93.368,28.572 93.719,27.871 93.701,27.334C93.703,27.05 93.588,26.374 93.271,26.671C93.187,27.033 92.913,28.297 92.641,28.498C92.869,20.596 87.762,27.9 86.319,24.565C85.246,24.924 86.175,27.987 85.823,28.487C85.533,28.278 85.342,27.044 85.267,26.671C85.202,26.622 85.142,26.582 85.091,26.602C84.501,27.233 84.984,28.44 85.73,28.963C85.676,30.897 87.905,33.139 89.271,32.846C90.629,33.136 92.833,30.939 92.811,29.003" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M92.606,28.215C92.608,28.14 92.609,28.072 92.609,28.001C92.611,28.079 92.611,28.159 92.611,28.24C92.609,28.231 92.608,28.224 92.606,28.215Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M94.445,53.869L94.454,52.995C93.056,52.2 90.721,51.843 89.213,52.807C88.121,53.813 89.652,55.127 90.567,55.263C92.001,55.56 93.31,55.168 94.447,54.84" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M37.907,102.23C37.821,105.053 32.717,104.326 33.925,101.262C34.838,97.432 33.763,98.591 37.229,98.44C37.395,98.763 37.933,101.309 37.907,102.23Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M23.629,100.483C24.574,99.939 27.646,98.777 27.152,97.522L29.814,97.113C29.838,97.595 31.193,101.084 29.557,101.407C28.234,101.671 19.878,103.923 23.629,100.483Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M39.057,58.003C39.057,58.003 39.354,61.322 39.406,61.953C39.891,67.87 38.27,93.425 37.835,97.239C37.805,97.501 37.662,98.306 37.203,98.48C36.264,98.835 34.81,98.48 34.81,98.48C34.548,98.48 34.332,98.271 34.319,98.005C34.124,93.724 32.158,68.971 31.862,66.684C30.706,70.943 30.5,92.32 30.02,97.707C29.994,98.01 29.743,98.242 29.442,98.242C29.442,98.242 28.234,98.478 27.658,98.242C27.049,97.992 26.978,97.219 26.951,96.985C25.703,86.038 23.568,68.081 24.82,57.092L29.228,59.026L39.057,58.003Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M35.573,28.767C35.573,28.767 28.774,28.298 28.77,28.305L28.641,28.737C28.243,27.427 26.694,23.771 28.69,22.615C29.929,19.639 36.833,20.987 36.613,24.941C36.692,25.406 35.573,28.767 35.573,28.767Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M22.011,55.428L21.104,55.499C20.426,56.994 20.273,59.404 21.372,60.854C22.477,61.875 23.663,60.201 23.72,59.258C23.893,57.772 23.385,56.473 22.957,55.347L22.011,55.428Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M21.322,43.713C21.512,42.651 21.753,41.618 22.051,40.625C22.701,38.153 22.962,36.499 24.918,35.179C25.728,34.633 26.785,34.252 28.234,33.761C28.758,33.583 29.125,33.261 29.235,32.819C29.371,32.28 33.454,32.509 34.468,32.617C34.616,32.633 34.618,32.498 34.681,32.637C34.868,33.051 35.217,33.583 36.089,33.938C37.269,34.417 40.129,35.36 41.213,37.302C42.897,40.321 45.69,52.379 43.78,53.693C42.923,54.283 39.759,54.772 39.245,54.861C39.242,56.416 39.231,58.584 39.092,58.911C38.003,61.455 28.645,62.019 24.733,59.139C24.308,58.827 24.848,48.075 25.536,43.281C25.461,43.6 25.38,43.947 25.298,44.314C24.872,46.217 24.569,48.478 24.219,50.783C23.875,53.052 23.696,55.748 23.394,56.033C22.413,56.957 21.727,56.907 21.055,56.724C20.665,56.618 20.342,56.287 20.33,55.877C20.205,51.315 20.613,47.673 21.322,43.713ZM39.368,51.001C39.93,50.783 40.176,50.886 40.453,50.783C40.494,49.455 39.214,43.993 39.057,43.513C39.057,43.536 39.268,47.134 39.368,51.001Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M31.748,48.064H34.15C34.462,48.064 34.717,48.321 34.717,48.641V54.582C34.717,54.9 34.464,55.159 34.15,55.159H31.792C31.479,55.159 31.224,54.902 31.224,54.582V48.648C31.224,48.327 31.476,48.066 31.786,48.066" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M32.003,34.312C30.937,34.097 29.758,33.434 29.259,32.768C29.423,32.144 29.688,31.214 29.669,31.09C29.938,31.383 34.065,31.39 34.337,31.09C34.318,31.214 34.583,32.144 34.747,32.768C34.244,33.434 33.07,34.096 32.003,34.312Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M35.589,28.914V28.836C36.124,28.457 36.461,27.771 36.444,27.246C36.446,26.968 36.336,26.306 36.03,26.597C35.95,26.952 35.686,28.188 35.424,28.384C35.644,20.653 30.734,27.799 29.346,24.537C28.315,24.888 29.208,27.884 28.87,28.374C28.59,28.17 28.407,26.962 28.336,26.597C28.273,26.549 28.215,26.51 28.166,26.53C27.599,27.147 28.063,28.327 28.781,28.84C28.728,30.732 30.872,32.925 32.185,32.638C33.49,32.922 35.61,30.773 35.589,28.879" /> + <path + android:fillColor="#E4B489" + android:pathData="M35.391,28.108C35.393,28.035 35.395,27.968 35.395,27.899C35.397,27.975 35.397,28.053 35.397,28.133C35.395,28.124 35.393,28.117 35.391,28.108Z" /> + <path + android:fillColor="#E3E3E3" + android:pathData="M37.159,53.206L37.168,52.352C35.824,51.573 33.579,51.224 32.129,52.167C31.08,53.152 32.551,54.437 33.431,54.57C34.81,54.861 36.068,54.478 37.161,54.157" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M61.232,133.102C61.093,137.727 52.786,136.536 54.752,131.515C56.238,125.24 54.488,127.14 60.13,126.893C60.4,127.422 61.275,131.594 61.232,133.102Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M37.996,130.241C39.533,129.349 44.533,127.446 43.729,125.389L48.062,124.718C48.102,125.508 50.306,131.226 47.644,131.755C45.491,132.188 31.891,135.878 37.996,130.241Z" /> + <path + android:fillColor="#8C8C98" + android:pathData="M63.105,60.632C63.105,60.632 63.588,66.071 63.673,67.106C64.463,76.801 61.823,118.673 61.116,124.923C61.068,125.353 60.835,126.672 60.088,126.957C58.559,127.538 56.192,126.957 56.192,126.957C55.766,126.957 55.414,126.614 55.394,126.178C55.076,119.165 51.877,78.605 51.394,74.857C49.513,81.836 49.178,116.863 48.397,125.69C48.354,126.187 47.945,126.567 47.456,126.567C47.456,126.567 45.49,126.954 44.553,126.567C43.561,126.158 43.445,124.891 43.402,124.508C41.371,106.57 37.896,77.147 39.933,59.139L47.107,62.309L63.105,60.632Z" /> + <path + android:fillColor="#4A4A4A" + android:pathData="M57.434,12.729C57.434,12.729 46.368,11.959 46.362,11.97L46.152,12.679C45.504,10.532 42.984,4.541 46.232,2.647C48.249,-2.228 59.485,-0.02 59.127,6.459C59.255,7.22 57.434,12.729 57.434,12.729Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M35.362,56.414L33.885,56.53C32.783,58.979 32.533,62.928 34.323,65.305C36.121,66.978 38.05,64.235 38.144,62.69C38.425,60.255 37.598,58.125 36.902,56.28L35.362,56.414Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M67.309,56.414L65.832,56.53C64.73,58.979 64.48,62.928 66.27,65.305C68.068,66.978 69.997,64.235 70.091,62.69C70.372,60.255 69.545,58.125 68.849,56.28L67.309,56.414Z" /> + <path + android:fillColor="#C66A61" + android:pathData="M34.24,37.219C34.55,35.478 34.942,33.785 35.428,32.158C36.485,28.107 36.911,25.397 40.093,23.235C41.411,22.34 43.132,21.715 45.491,20.911C46.343,20.62 46.939,20.091 47.118,19.368C47.34,18.485 53.985,18.86 55.636,19.037C55.877,19.063 55.88,18.842 55.982,19.069C56.286,19.749 56.855,20.62 58.275,21.201C60.196,21.986 64.849,23.531 66.614,26.713C69.355,31.661 70.926,56.135 69.1,57.504C68.015,58.318 66.362,58.318 65.449,57.504C64.536,53.397 64.08,43.813 62.254,36.51C64.08,55.679 63.389,61.586 63.162,62.121C61.389,66.29 46.158,67.214 39.791,62.496C39.101,61.984 39.979,44.366 41.098,36.51C40.976,37.033 40.845,37.602 40.712,38.204C40.019,41.321 39.524,45.026 38.956,48.803C38.396,52.521 38.104,56.938 37.612,57.405C36.016,58.919 34.899,58.838 33.805,58.539C33.172,58.364 32.646,57.821 32.626,57.15C32.422,49.674 33.087,43.707 34.24,37.219Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M51.624,21.814C49.888,21.463 47.971,20.376 47.158,19.284C47.425,18.261 47.857,16.739 47.826,16.535C48.263,17.014 54.979,17.026 55.423,16.535C55.391,16.739 55.823,18.261 56.09,19.284C55.272,20.376 53.36,21.46 51.624,21.814Z" /> + <path + android:fillColor="#F6B893" + android:pathData="M57.46,12.97V12.842C58.332,12.221 58.881,11.096 58.852,10.236C58.855,9.78 58.676,8.696 58.179,9.173C58.048,9.754 57.619,11.779 57.193,12.101C57.551,-0.566 49.559,11.143 47.3,5.797C45.621,6.372 47.076,11.282 46.525,12.084C46.07,11.75 45.772,9.771 45.656,9.173C45.553,9.094 45.459,9.03 45.38,9.062C44.457,10.073 45.212,12.009 46.38,12.848C46.295,15.948 49.784,19.542 51.92,19.072C54.045,19.536 57.494,16.015 57.46,12.912" /> + <path + android:fillColor="#DBF3FF" + android:pathData="M55.409,17.343C57.976,15.417 57.793,10.428 57.69,10.497C53.88,13.06 53.084,10.754 51.957,10.754C50.435,11.053 48.563,12.779 46.32,10.041C46.32,11.066 45.369,15.061 49.02,17.799C51.358,19.552 53.583,18.712 55.409,17.343Z" /> + <path + android:fillColor="#E4B489" + android:pathData="M57.139,11.648C57.141,11.529 57.144,11.418 57.144,11.305C57.147,11.43 57.147,11.558 57.147,11.689C57.144,11.674 57.141,11.662 57.139,11.648Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_trend_stable.xml b/Corona-Warn-App/src/main/res/drawable/ic_trend_stable.xml new file mode 100644 index 0000000000000000000000000000000000000000..96ed410bed427a88de041d4bba5206f6b96c7560 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_trend_stable.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="18dp" + android:height="18dp" + android:viewportWidth="18" + android:viewportHeight="20"> + <path + android:fillColor="#ffffff" + android:pathData="M15.7539,9.2734C15.918,9.4375 16,9.6471 16,9.9023C16,10.1393 15.918,10.3398 15.7539,10.5039L11.4062,14.8789C11.224,15.0612 11.0143,15.1523 10.7773,15.1523C10.5404,15.1523 10.3307,15.0612 10.1484,14.8789C9.9844,14.7148 9.9023,14.5143 9.9023,14.2773C9.9023,14.0221 9.9844,13.8125 10.1484,13.6484L13.0469,10.75H2.875C2.638,10.75 2.4284,10.668 2.2461,10.5039C2.082,10.3398 2,10.1302 2,9.875C2,9.638 2.082,9.4375 2.2461,9.2734C2.4284,9.0912 2.638,9 2.875,9H13.0469L10.1758,6.1289C9.9935,5.9466 9.9023,5.737 9.9023,5.5C9.9023,5.263 9.9935,5.0625 10.1758,4.8984C10.3398,4.7161 10.5404,4.625 10.7773,4.625C11.0143,4.625 11.2148,4.7161 11.3789,4.8984L15.7539,9.2734Z" /> +</vector> diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml index 6c84d3fc47baf97af47357c5b1bc12f8fe44e579..8fb6fceb7729465237cdc28fce7a9effabc43eae 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml @@ -8,10 +8,11 @@ <ImageView android:id="@+id/contact_diary_location_bottom_sheet_close_button" style="@style/buttonIcon" - android:layout_width="@dimen/circle_icon_big" - android:layout_height="@dimen/circle_icon_big" - android:layout_marginStart="@dimen/spacing_normal" + android:layout_width="@dimen/button_icon" + android:layout_height="@dimen/button_icon" + android:layout_marginStart="@dimen/spacing_tiny" android:layout_marginTop="@dimen/spacing_small" + android:padding="@dimen/spacing_mega_tiny" android:contentDescription="@string/accessibility_close" android:src="@drawable/ic_close" app:layout_constraintStart_toStartOf="parent" @@ -32,12 +33,12 @@ <ImageView android:id="@+id/contact_diary_location_bottom_sheet_delete_button" style="@style/buttonIcon" - android:layout_width="@dimen/circle_icon_big" - android:layout_height="@dimen/circle_icon_big" + android:layout_width="@dimen/button_icon" + android:layout_height="@dimen/button_icon" android:layout_marginTop="@dimen/spacing_small" - android:layout_marginEnd="@dimen/spacing_normal" + android:layout_marginEnd="@dimen/spacing_tiny" android:contentDescription="@string/contact_diary_delete_icon_content_description" - android:padding="@dimen/circle_icon_big_padding" + android:padding="@dimen/button_icon_padding" android:src="@drawable/ic_baseline_delete" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_onboarding_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_onboarding_fragment.xml index 4920c3a3aa81639c60aa0b668dab0b8931d71ffb..17ede5ce20afbd092cd2cfa987e677027c906bdd 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_onboarding_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_onboarding_fragment.xml @@ -138,6 +138,19 @@ app:layout_constraintStart_toStartOf="@id/guideline_start" app:layout_constraintTop_toBottomOf="@id/contact_diary_onboarding_fourth_section" /> + <include + android:id="@+id/contact_diary_onboarding_sixth_section" + layout="@layout/contact_diary_onboarding_row" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_normal" + android:focusable="true" + app:body="@{@string/contact_diary_onboarding_functionality_sixth_section}" + app:icon="@{@drawable/ic_high_risk_alert}" + app:layout_constraintEnd_toStartOf="@id/guideline_end" + app:layout_constraintStart_toStartOf="@id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/contact_diary_onboarding_fifth_section" /> + <include android:id="@+id/contact_diary_onboarding_privacy_card" layout="@layout/contact_diary_privacy_card" @@ -148,7 +161,7 @@ android:focusable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/contact_diary_onboarding_fifth_section" /> + app:layout_constraintTop_toBottomOf="@id/contact_diary_onboarding_sixth_section" /> <View android:id="@+id/contact_diary_onboarding_first_divider" diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml index eb730acfbfe14ee89383c591c9f77bb44bbe8e64..29ad99976dd13a46d9511d1faf77924cf0d9798a 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml @@ -11,75 +11,82 @@ android:layout_marginBottom="@dimen/spacing_tiny" android:focusable="true"> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/contact_diary_overview_element_item_body" - android:layout_width="match_parent" - android:layout_height="@dimen/contact_diary_list_item" + <TextView + android:id="@+id/contact_diary_overview_element_name" + style="@style/contactDiaryListItem" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_small" android:focusable="true" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/contact_diary_overview_element_guideline" + app:layout_constraintEnd_toStartOf="@+id/contact_diary_overview_element_right_arrow" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="parent" + tools:text="Donnerstag, 01.12.2020" /> - <TextView - android:id="@+id/contact_diary_overview_element_name" - style="@style/contactDiaryListItem" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/spacing_small" - android:focusable="true" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/contact_diary_overview_element_right_arrow" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="Donnerstag, 01.12.2020" /> - - <ImageView - android:id="@+id/contact_diary_overview_element_right_arrow" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginVertical="@dimen/spacing_tiny" - android:layout_marginEnd="@dimen/spacing_small" - android:importantForAccessibility="no" - android:scaleType="centerInside" - android:src="@drawable/ic_contact_diary_right_arrow" - app:layout_constraintBottom_toBottomOf="@+id/contact_diary_overview_element_name" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@+id/contact_diary_overview_element_name" /> + <ImageView + android:id="@+id/contact_diary_overview_element_right_arrow" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginVertical="@dimen/spacing_tiny" + android:layout_marginEnd="@dimen/spacing_small" + android:importantForAccessibility="no" + android:scaleType="centerInside" + android:src="@drawable/ic_contact_diary_right_arrow" + app:layout_constraintBottom_toTopOf="@id/contact_diary_overview_element_guideline" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - </androidx.constraintlayout.widget.ConstraintLayout> + <androidx.constraintlayout.widget.Guideline + android:id="@+id/contact_diary_overview_element_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/contact_diary_list_item" /> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/contact_diary_overview_nested_element_group" + <include + android:id="@+id/contact_diary_overview_nested_list_item_risk" + layout="@layout/contact_diary_overview_nested_list_item_risk" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/contact_diary_nested_recyclerview_margin" - android:focusable="true" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_element_guideline" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/contact_diary_overview_element_item_body"> + app:layout_constraintEnd_toEndOf="parent" /> - <View - android:id="@+id/contact_diary_overview_element_divider" - android:layout_width="match_parent" - android:layout_height="@dimen/card_divider" - android:background="?android:attr/listDivider" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + <androidx.constraintlayout.widget.Barrier + android:id="@+id/contact_diary_overview_barrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:barrierAllowsGoneWidgets="false" + app:constraint_referenced_ids="contact_diary_overview_element_guideline, contact_diary_overview_nested_list_item_risk"/> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/contact_diary_overview_nested_recycler_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:nestedScrollingEnabled="false" - android:paddingVertical="@dimen/spacing_tiny" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_element_divider" - tools:itemCount="2" - tools:listitem="@layout/contact_diary_person_list_item" /> + <View + android:id="@+id/contact_diary_overview_element_divider" + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:background="?android:attr/listDivider" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_barrier"/> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/contact_diary_overview_nested_recycler_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:nestedScrollingEnabled="false" + android:paddingVertical="@dimen/spacing_tiny" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_element_divider" /> - </androidx.constraintlayout.widget.ConstraintLayout> + <androidx.constraintlayout.widget.Group + android:id="@+id/contact_diary_overview_nested_element_group" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:constraint_referenced_ids="contact_diary_overview_element_divider,contact_diary_overview_nested_recycler_view"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item_risk.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item_risk.xml index 738952a4fc510fe0c8c0f00565975ff10e3f40d6..2ff42829ccaa4ab9502861e6a5195814643107f9 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item_risk.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item_risk.xml @@ -1,88 +1,60 @@ <?xml version="1.0" encoding="utf-8"?> -<layout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools"> - - <data> - - <variable - name="title" - type="String" /> - - <variable - name="body" - type="String" /> - - <variable - name="icon" - type="android.graphics.drawable.Drawable" /> - - </data> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/contact_diary_overview_risk_item" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/contact_diary_overview_risk_item" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:focusable="true" + android:paddingBottom="@dimen/spacing_small"> + + <View + android:id="@+id/contact_diary_overview_item_risk_divider" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginVertical="@dimen/spacing_tiny" - android:focusable="true" - android:paddingBottom="@dimen/spacing_tiny" - app:layout_constraintBottom_toBottomOf="parent" + android:layout_height="@dimen/card_divider" + android:layout_marginBottom="@dimen/spacing_small" + android:background="?android:attr/listDivider" + android:paddingBottom="@dimen/spacing_small" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <ImageView - android:id="@+id/contact_diary_overview_risk_item_image" - android:layout_width="wrap_content" - android:layout_height="0dp" - android:layout_marginStart="@dimen/spacing_small" - android:importantForAccessibility="no" - android:scaleType="centerInside" - android:src="@{icon}" - app:layout_constraintBaseline_toBaselineOf="@id/contact_diary_overview_item_risk_title" - app:layout_constraintEnd_toStartOf="@id/contact_diary_overview_item_risk_title" - app:layout_constraintStart_toStartOf="parent" - tools:src="@drawable/ic_high_risk_alert" /> - - <TextView - android:id="@+id/contact_diary_overview_item_risk_title" - style="@style/subtitle" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/spacing_small" - android:layout_marginEnd="@dimen/spacing_small" - android:focusable="true" - android:text="@{title}" - app:layout_constraintBottom_toBottomOf="@+id/contact_diary_overview_risk_item_image" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@+id/contact_diary_overview_risk_item_image" - app:layout_constraintTop_toTopOf="parent" - tools:text="Erhöhtes Risiko" /> - - <TextView - android:id="@+id/contact_diary_overview_item_risk_body" - style="@style/subtitleMedium" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_mega_tiny" - android:focusable="true" - android:text="@{body}" - app:layout_constraintEnd_toEndOf="@id/contact_diary_overview_item_risk_title" - app:layout_constraintStart_toStartOf="@+id/contact_diary_overview_item_risk_title" - app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_item_risk_title" - tools:text="aufgrund der von der App ausgewerteten Begegnungen. Diese müssen nicht in Zusammenhang mit den von Ihnen erfassten Personen und Orten stehen." /> - - <View - android:id="@+id/contact_diary_overview_item_risk_divider" - android:layout_width="match_parent" - android:layout_height="@dimen/card_divider" - android:layout_marginTop="@dimen/spacing_small" - android:layout_marginBottom="@dimen/spacing_tiny" - android:background="?android:attr/listDivider" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/contact_diary_overview_item_risk_body" /> - - </androidx.constraintlayout.widget.ConstraintLayout> + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/contact_diary_overview_risk_item_image" + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_marginStart="@dimen/spacing_small" + android:layout_marginTop="@dimen/spacing_small" + android:importantForAccessibility="no" + android:scaleType="centerInside" + app:layout_constraintEnd_toStartOf="@id/contact_diary_overview_item_risk_title" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_item_risk_divider" + tools:src="@drawable/ic_high_risk_alert" /> -</layout> \ No newline at end of file + <TextView + android:id="@+id/contact_diary_overview_item_risk_title" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_small" + android:layout_marginEnd="@dimen/spacing_small" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/contact_diary_overview_risk_item_image" + app:layout_constraintTop_toTopOf="@id/contact_diary_overview_risk_item_image" + tools:text="Erhöhtes Risiko" /> + + <TextView + android:id="@+id/contact_diary_overview_item_risk_body" + style="@style/subtitleMedium" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_mega_tiny" + android:focusable="true" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/contact_diary_overview_item_risk_title" + app:layout_constraintStart_toStartOf="@+id/contact_diary_overview_item_risk_title" + app:layout_constraintTop_toBottomOf="@id/contact_diary_overview_item_risk_title" + tools:text="aufgrund der von der App ausgewerteten Begegnungen. Diese müssen nicht in Zusammenhang mit den von Ihnen erfassten Personen und Orten stehen." /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml index c4ed71519a0afbfa26f186252bfd042fb1e14cae..202230fd6d7fbc51ea072e18361baa9ece99ebdf 100644 --- a/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml @@ -8,12 +8,13 @@ <ImageView android:id="@+id/contact_diary_person_bottom_sheet_close_button" style="@style/buttonIcon" - android:layout_width="@dimen/circle_icon_big" - android:layout_height="@dimen/circle_icon_big" - android:layout_marginStart="@dimen/spacing_normal" + android:layout_width="@dimen/button_icon" + android:layout_height="@dimen/button_icon" + android:layout_marginStart="@dimen/spacing_tiny" android:layout_marginTop="@dimen/spacing_small" android:contentDescription="@string/accessibility_close" android:src="@drawable/ic_close" + android:padding="@dimen/spacing_mega_tiny" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -31,12 +32,12 @@ <ImageView android:id="@+id/contact_diary_person_bottom_sheet_delete_button" style="@style/buttonIcon" - android:layout_width="@dimen/circle_icon_big" - android:layout_height="@dimen/circle_icon_big" + android:layout_width="@dimen/button_icon" + android:layout_height="@dimen/button_icon" android:layout_marginTop="@dimen/spacing_small" - android:layout_marginEnd="@dimen/spacing_normal" + android:layout_marginEnd="@dimen/spacing_tiny" android:contentDescription="@string/contact_diary_delete_icon_content_description" - android:padding="@dimen/circle_icon_big_padding" + android:padding="@dimen/button_icon_padding" android:src="@drawable/ic_baseline_delete" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_statistics_explanation.xml b/Corona-Warn-App/src/main/res/layout/fragment_statistics_explanation.xml new file mode 100644 index 0000000000000000000000000000000000000000..66bbd2becafe0aa1abc11b6ef4ed0a7b48a22dc3 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/fragment_statistics_explanation.xml @@ -0,0 +1,451 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/statistics_explanation_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/statistics_explanation_title" + tools:context=".statistics.ui.StatisticsExplanationFragment"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/statistics_explanation_title" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <include + android:id="@+id/statistics_explanation_header_button_back" + layout="@layout/include_button_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:focusable="true" + app:icon="@{@drawable/ic_close}" + app:iconDescription="@{@string/accessibility_back}" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/statistics_explanation_header_title" + style="@style/headline6" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="6dp" + android:accessibilityHeading="true" + android:focusable="true" + android:text="@string/statistics_explanation_title" + app:layout_constraintStart_toEndOf="@+id/statistics_explanation_header_button_back" + app:layout_constraintTop_toTopOf="@+id/statistics_explanation_header_button_back" + tools:text="@string/statistics_explanation_title" /> + + <TextView + android:id="@+id/statistics_explanation_header_subtitle" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="3dp" + android:focusable="true" + android:text="@string/statistics_explanation_subtitle" + app:layout_constraintStart_toEndOf="@+id/statistics_explanation_header_button_back" + app:layout_constraintTop_toBottomOf="@+id/statistics_explanation_header_title" + tools:text="@string/statistics_explanation_subtitle" /> + + <View + android:id="@+id/statistics_explanation_header_subtitle_divider" + android:layout_width="0dp" + android:layout_height="@dimen/card_divider" + android:layout_marginTop="13dp" + android:background="@color/colorHairline" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/statistics_explanation_header_subtitle" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_start" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="@dimen/guideline_start" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_end" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_end="@dimen/guideline_end" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_back" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="14dp" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <ScrollView + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="@id/guideline_bottom" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/statistics_explanation_title" + app:layout_constraintVertical_bias="1.0"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/interoperability_illustration" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="31dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_illustration_description" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_illustration_statistics_explanation" /> + + <TextView + style="@style/headline5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="41dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_confirmed_new_infection_title" + android:focusable="true" + android:text="@string/statistics_explanation_confirmed_new_infection_title" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_confirmed_new_infection_text" + android:focusable="true" + android:text="@string/statistics_explanation_confirmed_new_infection_text" /> + + <TextView + style="@style/headline5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="36dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_warned_persons_title" + android:focusable="true" + android:text="@string/statistics_explanation_warned_persons_title" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_warned_persons_text" + android:focusable="true" + android:text="@string/statistics_explanation_warned_persons_text" /> + + <TextView + style="@style/headline5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="36dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_seven_day_incidence_title" + android:focusable="true" + android:text="@string/statistics_explanation_seven_day_incidence_title" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_seven_day_incidence_text" + android:focusable="true" + android:text="@string/statistics_explanation_seven_day_incidence_text" /> + + <TextView + style="@style/headline5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="36dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_seven_day_r_value_title" + android:focusable="true" + android:text="@string/statistics_explanation_seven_day_r_value_title" /> + + <TextView + android:id="@+id/statistics_explanation_seven_day_r_value_text" + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_seven_day_r_value_text" + android:focusable="true" + android:text="@string/statistics_explanation_seven_day_r_value_text" /> + + <View + android:layout_width="match_parent" + android:layout_height="@dimen/card_divider" + android:layout_marginTop="43dp" + android:background="@color/colorHairline" /> + + <TextView + style="@style/headline5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="45dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_legend_title" + android:focusable="true" + android:text="@string/statistics_explanation_legend_title" /> + + <TextView + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="24dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_title" + android:focusable="true" + android:text="@string/statistics_explanation_period_title" /> + + <TextView + style="@style/subtitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="22dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_yesterday_subtitle" + android:focusable="true" + android:text="@string/statistics_explanation_period_yesterday_subtitle" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_yesterday_text" + android:focusable="true" + android:text="@string/statistics_explanation_period_yesterday_text" /> + + <TextView + style="@style/subtitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="22dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_seven_day_subtitle" + android:focusable="true" + android:text="@string/statistics_explanation_period_seven_day_subtitle" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_seven_day_text" + android:focusable="true" + android:text="@string/statistics_explanation_period_seven_day_text" /> + + <TextView + style="@style/subtitleMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="22dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_total_subtitle" + android:focusable="true" + android:text="@string/statistics_explanation_period_total_subtitle" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="8dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_period_total_text" + android:focusable="true" + android:text="@string/statistics_explanation_period_total_text" /> + + <TextView + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="47dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_trend_title" + android:focusable="true" + android:text="@string/statistics_explanation_trend_title" /> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="14dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_trend_text" + android:focusable="true" + android:text="@string/statistics_explanation_trend_text" /> + + <TextView + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="35dp" + android:layout_marginEnd="@dimen/guideline_end" + android:contentDescription="@string/statistics_explanation_trend_icons_title" + android:focusable="true" + android:text="@string/statistics_explanation_trend_icons_title" + android:textSize="14sp" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="25dp" + android:layout_marginEnd="@dimen/guideline_end"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/statistics_explanation_trend_increasing_arrow" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="@drawable/bg_statistics_trend_negative" + android:contentDescription="@string/statistics_explanation_trend_increasing_title" + android:padding="1dp" + android:rotation="-45" + android:scaleType="center" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_trend_stable" /> + + <TextView + android:id="@+id/statistics_explanation_trend_increasing_title" + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="34dp" + android:contentDescription="@string/statistics_explanation_trend_increasing_title" + android:focusable="true" + android:text="@string/statistics_explanation_trend_increasing_title" + app:layout_constraintStart_toEndOf="@+id/statistics_explanation_trend_increasing_arrow" + app:layout_constraintTop_toTopOf="@+id/statistics_explanation_trend_increasing_arrow" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/statistics_explanation_trend_decreasing_arrow" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="28dp" + android:background="@drawable/bg_statistics_trend_positive" + android:contentDescription="@string/statistics_explanation_trend_decreasing_title" + android:padding="1dp" + android:rotation="45" + android:scaleType="center" + app:layout_constraintStart_toStartOf="@+id/statistics_explanation_trend_increasing_arrow" + app:layout_constraintTop_toBottomOf="@+id/statistics_explanation_trend_increasing_title" + app:srcCompat="@drawable/ic_trend_stable" /> + + <TextView + android:id="@+id/statistics_explanation_trend_decreasing_title" + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="34dp" + android:contentDescription="@string/statistics_explanation_trend_decreasing_title" + android:focusable="true" + android:text="@string/statistics_explanation_trend_decreasing_title" + app:layout_constraintStart_toEndOf="@+id/statistics_explanation_trend_decreasing_arrow" + app:layout_constraintTop_toTopOf="@+id/statistics_explanation_trend_decreasing_arrow" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/statistics_explanation_trend_stable_arrow" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="28dp" + android:background="@drawable/bg_statistics_trend_neutral" + android:contentDescription="@string/statistics_explanation_trend_stable_title" + android:padding="1dp" + android:scaleType="center" + app:layout_constraintStart_toStartOf="@+id/statistics_explanation_trend_decreasing_arrow" + app:layout_constraintTop_toBottomOf="@+id/statistics_explanation_trend_decreasing_title" + app:srcCompat="@drawable/ic_trend_stable" /> + + <TextView + android:id="@+id/statistics_explanation_trend_stable_title" + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="34dp" + android:contentDescription="@string/statistics_explanation_trend_stable_title" + android:focusable="true" + android:text="@string/statistics_explanation_trend_stable_title" + app:layout_constraintStart_toEndOf="@+id/statistics_explanation_trend_stable_arrow" + app:layout_constraintTop_toTopOf="@+id/statistics_explanation_trend_stable_arrow" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <TextView + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/guideline_start" + android:layout_marginTop="24dp" + android:layout_marginEnd="@dimen/guideline_end" + android:layout_marginBottom="23dp" + android:contentDescription="@string/statistics_explanation_trend_description" + android:focusable="true" + android:text="@string/statistics_explanation_trend_description" /> + + </LinearLayout> + + </ScrollView> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="@dimen/guideline_bottom" /> + + <include layout="@layout/merge_guidelines_side" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + +</layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_dispatcher.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_dispatcher.xml index 218e142611efdb45c2ea6b7d148b98c8e324f838..f769e6fe74599e776179e946c7da286a1299f129 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_dispatcher.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_dispatcher.xml @@ -15,6 +15,7 @@ android:id="@+id/submission_dispatcher_header" layout="@layout/include_header" android:layout_width="0dp" + android:focusable="true" android:layout_height="wrap_content" app:icon="@{@drawable/ic_close}" app:layout_constraintEnd_toEndOf="parent" @@ -34,12 +35,13 @@ <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:focusable="true" android:paddingBottom="@dimen/spacing_normal"> <ImageView android:id="@+id/submission_dispatcher_illustration" android:layout_width="0dp" + android:contentDescription="@string/submission_intro_illustration_description" + android:focusable="true" android:layout_height="wrap_content" android:src="@drawable/ic_illustration_test" app:layout_constraintEnd_toEndOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml index f58fb7a894afde32bd80313d0c0761e27473838e..d21f46391ab64f4726c77fba18027349ed49fb62 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml @@ -7,22 +7,31 @@ <import type="de.rki.coronawarnapp.submission.Symptoms.StartOf" /> </data> - <LinearLayout + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> + android:layout_height="match_parent" + android:fillViewport="true"> <include android:id="@+id/submission_symptom_calendar_header" layout="@layout/include_header" android:layout_width="match_parent" + app:layout_constraintBottom_toTopOf="@+id/scrollView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" android:layout_height="wrap_content" app:icon="@{@drawable/ic_close}" app:title="@{@string/submission_symptom_calendar_title}" /> <ScrollView - android:layout_width="match_parent" - android:layout_height="match_parent"> + android:id="@+id/scrollView" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintBottom_toTopOf="@+id/symptom_button_next" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/submission_symptom_calendar_header"> <LinearLayout android:layout_width="match_parent" @@ -95,16 +104,23 @@ android:enabled="true" android:text="@string/submission_symptom_verify" /> - <Button - android:id="@+id/symptom_button_next" - style="@style/buttonPrimary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/spacing_small" - android:text="@string/submission_done_button_done" /> - </LinearLayout> </ScrollView> - </LinearLayout> + + <Button + android:id="@+id/symptom_button_next" + style="@style/buttonPrimary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:layout_marginVertical="@dimen/spacing_small" + android:layout_marginBottom="@dimen/spacing_small" + android:text="@string/submission_done_button_done" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/scrollView"/> + + </androidx.constraintlayout.widget.ConstraintLayout> </layout> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_intro.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_intro.xml index fd16d800a5f81b30bc6685e64c605956464bfcee..a2d7596344f3432824a3544d750532496e69b356 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_intro.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_intro.xml @@ -1,10 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> - <LinearLayout + + + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> + android:layout_height="match_parent" + android:fillViewport="true"> <include android:id="@+id/submission_symptom_header" @@ -12,11 +14,20 @@ android:layout_width="match_parent" android:layout_height="wrap_content" app:icon="@{@drawable/ic_close}" + app:layout_constraintBottom_toTopOf="@+id/scrollView" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" app:title="@{@string/submission_symptom_title}" /> <ScrollView - android:layout_width="match_parent" - android:layout_height="match_parent"> + android:id="@+id/scrollView" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintBottom_toTopOf="@+id/symptom_button_next" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/submission_symptom_header"> <LinearLayout android:id="@+id/submission_symptom_container" @@ -81,17 +92,21 @@ android:enabled="true" android:text="@string/submission_symptom_no_information_button" /> - <Button - android:id="@+id/symptom_button_next" - style="@style/buttonPrimary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_small" - android:layout_marginBottom="@dimen/spacing_small" - android:text="@string/submission_symptom_further_button" /> - </LinearLayout> </ScrollView> - </LinearLayout> + + <Button + android:id="@+id/symptom_button_next" + style="@style/buttonPrimary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:layout_marginVertical="@dimen/spacing_small" + android:text="@string/submission_symptom_further_button" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/scrollView" /> + </androidx.constraintlayout.widget.ConstraintLayout> </layout> diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_basecard_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_basecard_layout.xml index 27fd7d3aa442ab98ecd79aea7e70e4e407781bfc..566e9e80d6932d397c29065cd08b2b74644e65a7 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_basecard_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_basecard_layout.xml @@ -2,10 +2,12 @@ <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/card_container" - android:layout_width="328dp" - android:layout_height="300dp" + android:layout_width="@dimen/statistics_card_width" + android:layout_height="wrap_content" + android:minHeight="@dimen/statistics_card_height" android:backgroundTint="@color/colorSurface1" android:focusable="true" android:foreground="?selectableItemBackground" app:cardCornerRadius="@dimen/radius_card" - app:cardElevation="@dimen/elevation_strong" /> + android:layout_marginVertical="@dimen/spacing_tiny" + app:cardElevation="@dimen/elevation_weak" /> diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_incidence_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_incidence_layout.xml index ae7412a6d9032753eefe6f04a1bcc1ee1e7e81c5..de5b14693dc5b51ec89f6dcfa6cd3fcf200a8ee0 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_incidence_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_incidence_layout.xml @@ -1,24 +1,109 @@ <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools"> + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="MissingConstraints"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" + tools:layout_height="wrap_content" + tools:layout_width="@dimen/statistics_card_width" tools:showIn="@layout/home_statistics_cards_basecard_layout"> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/background_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + android:src="@drawable/ic_statistics_incidence" + android:paddingStart="0dp" + android:paddingEnd="@dimen/spacing_small" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/flow_layout" /> + <TextView android:id="@+id/title" style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:focusable="false" - android:text="Incidence" + android:layout_marginStart="@dimen/spacing_normal" + android:layout_marginTop="@dimen/spacing_normal" + android:text="@string/statistics_explanation_seven_day_incidence_title" + app:layout_constraintEnd_toStartOf="@id/info_statistics" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/primary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Bis gestern" /> + + <TextView + android:id="@+id/primary_value" + style="@style/StatisticsCardPrimaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + tools:text="98,9" /> + + <TextView + android:id="@+id/secondary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_normal" + android:text="@string/statistics_card_incidence_value_description" /> + + <de.rki.coronawarnapp.statistics.ui.TrendArrowView + android:id="@+id/trend_arrow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_tiny" + app:layout_constraintBottom_toBottomOf="@id/primary_value" + app:layout_constraintStart_toEndOf="@id/primary_value" + app:layout_constraintTop_toTopOf="@id/primary_value" /> + + <ImageButton + android:id="@+id/info_statistics" + style="@style/StatisticsCardInfoButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/horizontal_guideline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/guideline_statistics_card_content" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/content_barrier" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="title,horizontal_guideline" /> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow_layout" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="@dimen/card_padding" + android:paddingBottom="@dimen/spacing_small" + app:constraint_referenced_ids="primary_label,primary_value,secondary_label" + app:flow_horizontalAlign="start" + app:flow_horizontalBias="0" + app:flow_maxElementsWrap="1" + app:flow_wrapMode="chain" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/background_image" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/content_barrier" /> + </androidx.constraintlayout.widget.ConstraintLayout> </layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_infections_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_infections_layout.xml index d9241d6970d7e87074b61f67f03eac77f62bf695..5d227e91ffc571b3db20c4f1d8d9a3f3e2491ed8 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_infections_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_infections_layout.xml @@ -1,25 +1,129 @@ <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools"> + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="MissingConstraints"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + tools:layout_height="wrap_content" + tools:layout_width="@dimen/statistics_card_width" tools:showIn="@layout/home_statistics_cards_basecard_layout"> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/background_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + android:src="@drawable/ic_main_illustration_infection" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/flow_layout" /> + <TextView android:id="@+id/title" style="@style/headline5" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:focusable="false" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:text="Infections" + android:layout_marginStart="@dimen/spacing_normal" + android:layout_marginTop="@dimen/spacing_normal" + android:text="@string/statistics_card_infections_title" + app:layout_constraintEnd_toStartOf="@id/info_statistics" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + <TextView + android:id="@+id/primary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Gestern" /> + + <TextView + android:id="@+id/primary_value" + style="@style/StatisticsCardPrimaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + tools:text="14.714" /> + + <TextView + android:id="@+id/secondary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_normal" + android:text="@string/statistics_card_infections_secondary_label" /> + + <TextView + android:id="@+id/secondary_value" + style="@style/StatisticsCardSecondaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="11.981" /> + + <de.rki.coronawarnapp.statistics.ui.TrendArrowView + android:id="@+id/trend_arrow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_tiny" + app:layout_constraintBottom_toBottomOf="@id/secondary_value" + app:layout_constraintStart_toEndOf="@id/secondary_value" + app:layout_constraintTop_toTopOf="@id/secondary_value" /> + + <TextView + android:id="@+id/tertiary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_tiny" + android:text="@string/statistics_card_infections_tertiary_label" /> + + <TextView + android:id="@+id/tertiary_value" + style="@style/StatisticsCardSecondaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="429.181" /> + + <ImageButton + android:id="@+id/info_statistics" + style="@style/StatisticsCardInfoButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/horizontal_guideline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/guideline_statistics_card_content" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/content_barrier" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="title,horizontal_guideline" /> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow_layout" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="@dimen/card_padding" + android:paddingBottom="@dimen/spacing_small" + app:constraint_referenced_ids="primary_label,primary_value,secondary_label,secondary_value,tertiary_label,tertiary_value" + app:flow_horizontalAlign="start" + app:flow_horizontalBias="0" + app:flow_maxElementsWrap="1" + app:flow_wrapMode="chain" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/background_image" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/content_barrier" /> + </androidx.constraintlayout.widget.ConstraintLayout> </layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_keysubmissions_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_keysubmissions_layout.xml index 49b8924be0ca30b0eeb5aadcc8d49bd609db4792..44b537c3567bb9b5bafe1a3f09fda373c8b5a43b 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_keysubmissions_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_keysubmissions_layout.xml @@ -1,24 +1,141 @@ <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools"> + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="MissingConstraints"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" + tools:layout_height="wrap_content" + tools:layout_width="@dimen/statistics_card_width" tools:showIn="@layout/home_statistics_cards_basecard_layout"> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/background_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + android:src="@drawable/ic_main_illustration_warnende_personen" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/flow_layout" /> + <TextView android:id="@+id/title" style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="24dp" - android:layout_marginTop="24dp" - android:focusable="false" - android:text="Key Submissions" + android:layout_marginStart="@dimen/spacing_normal" + android:layout_marginTop="@dimen/spacing_normal" + android:text="@string/statistics_card_submission_title" + app:layout_constraintEnd_toStartOf="@id/info_statistics" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/primary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Gestern" /> + + <TextView + android:id="@+id/primary_value" + style="@style/StatisticsCardPrimaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + tools:text="1.514" /> + + <TextView + android:id="@+id/secondary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_normal" + android:text="@string/statistics_card_infections_secondary_label" /> + + <TextView + android:id="@+id/secondary_value" + style="@style/StatisticsCardSecondaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="1.812" /> + + <de.rki.coronawarnapp.statistics.ui.TrendArrowView + android:id="@+id/trend_arrow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_tiny" + app:layout_constraintBottom_toBottomOf="@id/secondary_value" + app:layout_constraintStart_toEndOf="@id/secondary_value" + app:layout_constraintTop_toTopOf="@id/secondary_value" /> + + <TextView + android:id="@+id/tertiary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_tiny" + android:text="@string/statistics_card_infections_tertiary_label" /> + + <TextView + android:id="@+id/tertiary_value" + style="@style/StatisticsCardSecondaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:text="20.922" /> + + <ImageButton + android:id="@+id/info_statistics" + style="@style/StatisticsCardInfoButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/footnote" + style="@style/StatisticsCardValueLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginBottom="@dimen/spacing_small" + android:text="@string/statistics_card_submission_bottom_text" + android:textSize="@dimen/font_small" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/flow_layout" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/horizontal_guideline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/guideline_statistics_card_content" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/content_barrier" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="title,horizontal_guideline" /> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow_layout" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="@dimen/card_padding" + android:paddingBottom="@dimen/spacing_small" + app:constraint_referenced_ids="primary_label,primary_value,secondary_label,secondary_value,tertiary_label,tertiary_value" + app:flow_horizontalAlign="start" + app:flow_horizontalBias="0" + app:flow_maxElementsWrap="1" + app:flow_wrapMode="chain" + app:layout_constraintBottom_toTopOf="@id/footnote" + app:layout_constraintEnd_toStartOf="@id/background_image" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/content_barrier" /> + </androidx.constraintlayout.widget.ConstraintLayout> </layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_sevendayrvalue_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_sevendayrvalue_layout.xml new file mode 100644 index 0000000000000000000000000000000000000000..d32cc602176b1c3b7a17d1e94a940ba5d5ee933b --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_sevendayrvalue_layout.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="MissingConstraints"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:layout_height="wrap_content" + tools:layout_width="@dimen/statistics_card_width" + tools:showIn="@layout/home_statistics_cards_basecard_layout"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/background_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + android:src="@drawable/ic_7_day_r_value" + android:paddingStart="@dimen/spacing_normal" + android:paddingEnd="@dimen/spacing_normal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/flow_layout" /> + + <TextView + android:id="@+id/title" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_normal" + android:layout_marginTop="@dimen/spacing_normal" + android:text="@string/statistics_title_reproduction" + app:layout_constraintEnd_toStartOf="@id/info_statistics" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/primary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + tools:text="Aktuell" /> + + <TextView + android:id="@+id/primary_value" + style="@style/StatisticsCardPrimaryValue" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + tools:text="1,04" /> + + <TextView + android:id="@+id/secondary_label" + style="@style/StatisticsCardValueLabel" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingTop="@dimen/spacing_normal" + android:text="@string/statistics_reproduction_average" /> + + <de.rki.coronawarnapp.statistics.ui.TrendArrowView + android:id="@+id/trend_arrow" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_tiny" + app:layout_constraintBottom_toBottomOf="@id/primary_value" + app:layout_constraintStart_toEndOf="@id/primary_value" + app:layout_constraintTop_toTopOf="@id/primary_value" /> + + <ImageButton + android:id="@+id/info_statistics" + style="@style/StatisticsCardInfoButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/horizontal_guideline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="@dimen/guideline_statistics_card_content" /> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/content_barrier" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:barrierDirection="bottom" + app:constraint_referenced_ids="title,horizontal_guideline" /> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow_layout" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="@dimen/card_padding" + android:paddingBottom="@dimen/spacing_small" + app:constraint_referenced_ids="primary_label,primary_value,secondary_label" + app:flow_horizontalAlign="start" + app:flow_horizontalBias="0" + app:flow_maxElementsWrap="1" + app:flow_wrapMode="chain" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/background_image" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/content_barrier" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + +</layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_scrollcontainer.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_scrollcontainer.xml index 0754f5a43e043b2d78d8c65671a8b48d3d562f4c..492a62886dece87dafeae2f3f36a148b5c7395a9 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_scrollcontainer.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_scrollcontainer.xml @@ -2,6 +2,4 @@ <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/statistics_recyclerview" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_tiny" - android:layout_marginBottom="@dimen/spacing_tiny" /> + android:layout_height="wrap_content" /> diff --git a/Corona-Warn-App/src/main/res/layout/new_release_info_screen_fragment.xml b/Corona-Warn-App/src/main/res/layout/new_release_info_screen_fragment.xml index c2b2eeeb9c1936af00a665ff3289079d57cf5798..87689a0685a480ffee62641edcbb11ecf2536810 100644 --- a/Corona-Warn-App/src/main/res/layout/new_release_info_screen_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/new_release_info_screen_fragment.xml @@ -85,12 +85,12 @@ android:layout_marginTop="@dimen/spacing_medium" android:layout_marginStart="@dimen/spacing_normal" android:layout_marginEnd="@dimen/spacing_normal" - android:text="@string/release_info_encounter_history_title" + android:text="@string/release_info_1_12_encounter_history_title" android:focusable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/bullet_point_encounter" app:layout_constraintTop_toBottomOf="@id/new_release_info_body" - tools:text="@string/release_info_encounter_history_title"/> + tools:text="@string/release_info_1_12_encounter_history_title"/> <ImageView android:id="@+id/bullet_point_encounter" @@ -112,11 +112,11 @@ android:layout_marginTop="@dimen/spacing_tiny" android:layout_marginEnd="@dimen/spacing_large" android:focusable="true" - android:text="@string/release_info_encounter_history_body" + android:text="@string/release_info_1_12_encounter_history_body" app:layout_constraintTop_toBottomOf="@+id/new_release_info_encounter_history_title" app:layout_constraintStart_toStartOf="@+id/new_release_info_encounter_history_title" app:layout_constraintEnd_toEndOf="parent" - tools:text="@string/release_info_encounter_history_body"/> + tools:text="@string/release_info_1_12_encounter_history_body"/> <TextView android:id="@+id/new_release_info_statistics_title" @@ -127,12 +127,12 @@ android:layout_marginTop="@dimen/spacing_medium" android:layout_marginStart="@dimen/spacing_normal" android:layout_marginEnd="@dimen/spacing_normal" - android:text="@string/release_info_statistics_title" + android:text="@string/release_info_1_12_statistics_title" android:focusable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@id/bullet_point_statistics" app:layout_constraintTop_toBottomOf="@id/new_release_info_encounter_history_body" - tools:text="@string/release_info_statistics_title"/> + tools:text="@string/release_info_1_12_statistics_title"/> <ImageView android:id="@+id/bullet_point_statistics" @@ -154,11 +154,11 @@ android:layout_marginTop="@dimen/spacing_tiny" android:layout_marginEnd="@dimen/spacing_large" android:focusable="true" - android:text="@string/release_info_statistics_body" + android:text="@string/release_info_1_12_statistics_body" app:layout_constraintTop_toBottomOf="@+id/new_release_info_statistics_title" app:layout_constraintStart_toStartOf="@+id/new_release_info_statistics_title" app:layout_constraintEnd_toEndOf="parent" - tools:text="@string/release_info_statistics_body"/> + tools:text="@string/release_info_1_12_statistics_body"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/Corona-Warn-App/src/main/res/layout/statistics_trend_view.xml b/Corona-Warn-App/src/main/res/layout/statistics_trend_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..d9c7fca9a466a4cd532a5facac95eb95f768c1e2 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/statistics_trend_view.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + + <ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/trend" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="1dp" + android:background="@drawable/bg_statistics_trend_neutral" + android:scaleType="center" + android:src="@drawable/ic_trend_stable" /> + diff --git a/Corona-Warn-App/src/main/res/layout/tracing_details_item_riskdetails_low_view.xml b/Corona-Warn-App/src/main/res/layout/tracing_details_item_riskdetails_low_view.xml index b7bd863894b75b95ea563758224d97dceceaf782..87a3ea1144a796a67b3b2c9919c0f74891c88a48 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_details_item_riskdetails_low_view.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_details_item_riskdetails_low_view.xml @@ -52,19 +52,5 @@ app:layout_constraintTop_toBottomOf="@+id/risk_details_information_subtitle" tools:text="@string/risk_details_information_body_low_risk_with_encounter" /> - <TextView - android:id="@+id/risk_details_information_lowrisk_body_url" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - android:clickable="true" - android:focusable="true" - android:linksClickable="true" - android:text="@string/risk_details_explanation_dialog_faq_body" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/risk_details_information_body" /> - - </androidx.constraintlayout.widget.ConstraintLayout> </layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index 63f88702b91bef501c8116e74f14b0d7b16de639..824fcbae891e8c306a468ef12b1bfd3b8b2aa064 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -55,6 +55,9 @@ <action android:id="@+id/action_mainFragment_to_submissionTestResultAvailableFragment" app:destination="@id/submissionTestResultAvailableFragment" /> + <action + android:id="@+id/action_mainFragment_to_statisticsExplanationFragment" + app:destination="@id/statisticsExplanationFragment" /> </fragment> <fragment @@ -218,6 +221,14 @@ android:id="@+id/action_riskDetailsFragment_to_settingsTracingFragment" app:destination="@id/settingsTracingFragment" /> </fragment> + + + <fragment + android:id="@+id/statisticsExplanationFragment" + android:name="de.rki.coronawarnapp.statistics.ui.StatisticsExplanationFragment" + android:label="@layout/fragment_statistics_explanation" + tools:layout="@layout/fragment_statistics_explanation" /> + <!-- Submission --> <fragment android:id="@+id/submissionDispatcherFragment" diff --git a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml index be9f59f02d89996739ac0f473595c76af9e1c632..177162a554f272cef015a48a0c8ef322a018ac2d 100644 --- a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml @@ -110,6 +110,8 @@ <string name="accessibility_location_selected">"ÐœÑÑто %s е избрано"</string> <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"Лице %s е избрано"</string> + <!-- XTXT: Day View headline (description for screen readers) --> + <string name="accessibility_day_view_header">"Дневен изглед"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Избор"</string> diff --git a/Corona-Warn-App/src/main/res/values-bg/strings.xml b/Corona-Warn-App/src/main/res/values-bg/strings.xml index 00d2b8211655403552c64908cdc31b6ccf763fd1..a39290eb503d864b695e8f8106e9fe571681702b 100644 --- a/Corona-Warn-App/src/main/res/values-bg/strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/strings.xml @@ -1273,6 +1273,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Подробна Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° обработването на данни и Вашето ÑъглаÑие"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7-дневна заболеваемоÑÑ‚"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"Потвърдени нови Ñлучаи на заразÑване на 100 000 души"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Потвърдени нови Ñлучаи на заразÑване"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"Средно за поÑледните 7 дни"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Общо"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Лица, изпратили предупреждениÑ"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"ОтноÑно приложението Corona-Warn-App"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"ДнеÑ"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Вчера"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"До днеÑ"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"До вчера"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Ð’ момента"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"До %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">" милиона"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Показатели"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"ПоÑÑнение на ÑтатиÑтиката"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Потвърдени нови Ñлучаи на заразÑване"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Брой диагноÑтицирани Ñ ÐºÐ¾Ñ€Ð¾Ð½Ð°Ð²Ð¸Ñ€ÑƒÑ Ð»Ð¸Ñ†Ð°, региÑтрирани в инÑтитута “Роберт Кохâ€"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Лица, изпратили предупреждениÑ"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Брой потребители, диагноÑтицирани Ñ ÐºÐ¾Ñ€Ð¾Ð½Ð°Ð²Ð¸Ñ€ÑƒÑ, които Ñа предупредили другите потребители чрез приложението"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7-дневна заболеваемоÑÑ‚"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Общ брой потвърдени нови Ñлучаи на заразÑване за поÑледните 7 дни (по дата на региÑтриране) на 100 000 души"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7-дневно репродуктивно чиÑло R"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"Репродуктивното чиÑло R указва Ñредно колко души заразÑва едно заразено лице. Текущата ÑтойноÑÑ‚ взема предвид данните от поÑледните 5 дни. \n\nЗа повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð²Ð¸Ð¶Ñ‚Ðµ\nЧЗВ отноÑно ÑтатиÑтиката."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"ЧЗВ отноÑно ÑтатиÑтиката."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Легенда"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Период от време"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Вчера/до вчера"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Брой за Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ Ð´ÐµÐ½/до Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ Ð´ÐµÐ½. Ðко вÑе още нÑма данни за Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ Ð´ÐµÐ½, Ñе показва поÑледната дата, за коÑто има данни."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7 дни Ñредно"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Средна ÑтойноÑÑ‚ за поÑледните 7 дни"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Общо"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Общ брой от началото на годината или от Ñтартирането на приложението (15 юни 2020 г.)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"ТенденциÑ"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"ПоÑоката на Ñтрелката указва дали тенденциÑта е към нараÑтване, намалÑване или Ñтабилизиране, Ñ‚.е. отклонението е по-малко от 1\% ÑпрÑмо Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ Ð´ÐµÐ½ или от 5\% ÑпрÑмо предишната Ñедмица. Цветът указва тенденциÑта като положителна (зелено), отрицателна (червено) или неутрална (Ñиво). За определÑне на Ñ‚eнденциÑта ÑтойноÑтта от Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ Ð´ÐµÐ½ Ñе ÑравнÑва Ñ Ñ‚Ð°Ð·Ð¸ от преди два дни, а при 7-дневната Ñ‚ÐµÐ½Ð´ÐµÐ½Ñ†Ð¸Ñ Ñредната ÑтойноÑÑ‚ за поÑледните 7 дни Ñе ÑравнÑва ÑÑŠÑ Ñъответната ÑтойноÑÑ‚ за Ð¿Ñ€ÐµÐ´Ð¸ÑˆÐ½Ð¸Ñ 7-дневен период."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"Възможни Ñа Ñледните тенденции:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"ТенденциÑ: Ðагоре"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"ТенденциÑ: Ðадолу"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"ТенденциÑ: Стабилизиране"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"Оценката на тенденциÑта за Ð±Ñ€Ð¾Ñ Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¾Ñ‚ потребители на приложението Ñе Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ñ Ð² завиÑимоÑÑ‚ от текущите нива на заразÑване, поради което тази Ñ‚ÐµÐ½Ð´ÐµÐ½Ñ†Ð¸Ñ Ð²Ð¸Ð½Ð°Ð³Ð¸ Ñе показва като неутрална."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"ÐбÑтрактно изображение на Ñмартфон Ñ 4 контейнера за данни"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1496,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Отказ"</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"Още информациÑ"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"7-дневно репродуктивно чиÑло R"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Ð’ момента"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"Ñреден брой заразени от вÑÑко заразено лице"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"ТенденциÑ: Ðагоре"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"ТенденциÑ: Ðадолу"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"ТенденциÑ: Стабилизиране"</string> + </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml index 819bdd358ae9416838d697c52dcf772621a1d2c5..aa7b5d6ed3933f90c69acf2299fe2b61f940d709 100644 --- a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml @@ -41,6 +41,8 @@ <string name="contact_diary_onboarding_functionality_fourth_section">"Sie können hinzugefügte Personen und Orte jederzeit wieder aus dem Tagebuch entfernen. Tagebuch-Einträge werden nach 16 Tagen automatisch gelöscht."</string> <!-- XTXT: Contact diary onboarding screen fifth functionality --> <string name="contact_diary_onboarding_functionality_fifth_section">"Sie können Ihr Kontakt-Tagebuch im Textformat exportieren. So können Sie bei Bedarf Ihre Einträge ausdrucken, bearbeiten oder dem Gesundheitsamt zur Verfügung stellen."</string> + <!-- XTXT: Contact diary onboarding screen sixth functionality --> + <string name="contact_diary_onboarding_functionality_sixth_section">"Die angezeigten Risiko-Begegnungen müssen nicht in Zusammenhang mit den von Ihnen erfassten Personen und Orten stehen. Bitte ziehen Sie keine falschen Rückschlüsse."</string> <!-- XTXT: Title for the contact diary onboarding screen --> <string name="contact_diary_title">"Kontakt-Tagebuch"</string> <!-- XTXT: Body for legal information of the contact diary onboarding screen --> diff --git a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml index fd997738a8d07c43d842874bf44b22878424cd19..cf4463359ab5760793ee99be9c6ad7e8cf517323 100644 --- a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml @@ -7,17 +7,17 @@ <!-- XHED: Title for the release info screen --> <string name="release_info_header">"Neue Funktionen"</string> <!-- XHED: Version title for the release info screen --> - <string name="release_info_version_title">"Release <xliff:g id="release_app_version" example="1.12">"%1$d"</xliff:g></string> + <string name="release_info_version_title">"Release %1$d"</string> <!-- XTXT: Description for the release info screen --> <string name="release_info_version_body">"Mit diesem Update stellen wir Ihnen neben Fehlerbehebungen auch neue und erweiterte Funktionen zur Verfügung."</string> <!-- XTXT: Encounter history title for the release info screen --> - <string name="release_info_encounter_history_title">"Begegnungshistorie"</string> + <string name="release_info_1_12_encounter_history_title">"Begegnungshistorie"</string> <!-- XTXT: Encounter history body for the release info screen --> - <string name="release_info_encounter_history_body">"Sie können in Ihrem Kontakt-Tagebuch sehen, an welchen Tagen Sie ein niedriges oder erhöhtes Risiko aufgrund der von der App ausgewerteten Begegnungen hatten. Sie können damit Personen warnen, die Sie an diesen Tagen begleitet haben, wenn diese die Corona-Warn-App nicht nutzen. Diese Personen waren möglicherweise einem vergleichbaren Risiko ausgesetzt. Die Personen und Orte in Ihrem Kontakt-Tagebuch müssen nicht in Zusammenhang mit den angezeigten Risiko-Begegnungen stehen."</string> + <string name="release_info_1_12_encounter_history_body">"Sie können in Ihrem Kontakt-Tagebuch sehen, an welchen Tagen Sie ein niedriges oder erhöhtes Risiko aufgrund der von der App ausgewerteten Begegnungen hatten. Sie können damit Personen warnen, die Sie an diesen Tagen begleitet haben, wenn diese die Corona-Warn-App nicht nutzen. Diese Personen waren möglicherweise einem vergleichbaren Risiko ausgesetzt. Die Personen und Orte in Ihrem Kontakt-Tagebuch müssen nicht in Zusammenhang mit den angezeigten Risiko-Begegnungen stehen."</string> <!-- XTXT: Statistics title for the release info screen --> - <string name="release_info_statistics_title">"Statistiken"</string> + <string name="release_info_1_12_statistics_title">"Statistiken"</string> <!-- XTXT: Statistics body for the release info screen --> - <string name="release_info_statistics_body">"Sie bekommen einen Ãœberblick mit aktuellen Kennzahlen zum Infektionsgeschehen in Deutschland."</string> + <string name="release_info_1_12_statistics_body">"Sie bekommen einen Ãœberblick mit aktuellen Kennzahlen zum Infektionsgeschehen in Deutschland."</string> <!-- XBUT: Continue button for the release info screen --> <string name="release_info_continue_button">"Weiter"</string> 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 ebf9331883839d0fffdc360fb53c9a0c89ef6596..e34cff20fee89ef9a0c0ab4eee6c0126c01239a7 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -23,12 +23,6 @@ <!-- NOTR --> <string name="preference_timestamp_manual_diagnosis_keys_retrieval"><xliff:g id="preference">"preference_timestamp_manual_diagnosis_keys_retrieval"</xliff:g></string> <!-- NOTR --> - <string name="preference_background_job_allowed"><xliff:g id="preference">"preference_background_job_enabled"</xliff:g></string> - <!-- NOTR --> - <string name="preference_mobile_data_allowed"><xliff:g id="preference">"preference_mobile_data_enabled"</xliff:g></string> - <!-- NOTR --> - <string name="preference_registration_token"><xliff:g id="preference">"preference_registration_token"</xliff:g></string> - <!-- NOTR --> <string name="preference_device_pairing_successful_time"><xliff:g id="preference">"preference_device_pairing_successful_time"</xliff:g></string> <!-- NOTR --> <string name="preference_initial_tracing_activation_time"><xliff:g id="preference">"preference_initial_tracing_activation_time"</xliff:g></string> @@ -355,9 +349,6 @@ <string name="risk_details_explanation_dialog_title">"Information zur Funktionsweise der Risiko-Ermittlung"</string> <!-- YTXT: one time risk explanation dialog - pointing to the faq page for more information--> <string name="risk_details_explanation_dialog_faq_body">"Weitere Informationen finden Sie in den FAQ."</string> - <!-- XLNK: FAQ URL pointing to the faq page in german. Need to use the URL for english for all other languages--> - <string name="risk_details_explanation_faq_link">"https://www.coronawarn.app/de/faq/#encounter_but_green"</string> - <!-- XHED: risk details - deadman notification title --> <string name="risk_details_deadman_notification_title">"Ihr Risikostatus"</string> <!-- YTXT: risk details - deadman notification text --> @@ -771,7 +762,7 @@ <!-- XHED: Dialog title for generic web request error --> <string name="submission_error_dialog_web_generic_error_title">"Fehler"</string> <!-- XMSG: Dialog body for generic web request network error with status code --> - <string name="submission_error_dialog_web_generic_network_error_body">"Es ist ein Verbindungsfehler aufgetreten (%1$d). Bitte versuchen Sie es erneut."</string> + <string name="submission_error_dialog_web_generic_network_error_body">"Möglicherweise wurde Ihre Internet-Verbindung unterbrochen. Bitte stellen Sie sicher, dass Sie mit dem Internet verbunden sind."</string> <!-- XMSG: Dialog body for generic web request error without status code --> <string name="submission_error_dialog_web_generic_error_body">"Es ist ein Verbindungsfehler aufgetreten. Bitte versuchen Sie es erneut."</string> <!-- XBUT: Positive button for generic web request error --> @@ -963,6 +954,9 @@ <!-- Submission Intro --> <!-- XBUT: Submission introduction next button--> <string name="submission_intro_button_next">"Weiter"</string> + <!-- YTXT: Description for illustration in submission onboarding--> + <string name="submission_intro_illustration_description">"Ein positiver Testbefund wird verschlüsselt ins System übermittelt, das nun andere Nutzerinnen und Nutzer warnt."</string> + <!-- Dispatcher --> <!-- XHED: Page headline for dispatcher menu --> @@ -1276,6 +1270,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Ausführliche Informationen zur Datenverarbeitung und Ihrem Einverständnis"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7-Tage-Inzidenz"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"bestätigte Neuinfektionen je 100.000 Einwohner"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Bestätigte Neuinfektionen"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"7-Tage-Mittelwert"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Gesamt"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Warnende Personen"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"über die Corona-Warn-App"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Heute"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Gestern"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Bis heute"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Bis gestern"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Aktuell"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"Bis %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"Mio."</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Kennzahlen"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"Erklärung der Statistiken"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Bestätigte Neuinfektionen"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Anzahl der an das RKI übermittelten Corona-positiv getesteten Personen"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Warnende Personen"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Anzahl der Corona-positiv getesteten Personen, die mithilfe der App ihre Mitmenschen gewarnt haben"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7-Tage-Inzidenz"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Gesamtzahl der bestätigten Neuinfektionen der letzten 7 Tage (nach Meldedatum) pro 100.000 Einwohner"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7-Tage-R-Wert"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"Die Reproduktionszahl R gibt an, wie viele Personen im Durchschnitt von einer infizierten Person angesteckt wurden. Der aktuelle Wert berücksichtigt Daten bis vor 5 Tagen.\n\nWeitere Informationen finden Sie in der\nFAQ zu den Statistiken."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"FAQ zu den Statistiken."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Legende"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Zeitraum"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Gestern / Bis gestern"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Zahl für den Vortag / bis zum Vortag. Wenn für den Vortag noch keine Zahl vorliegt, wird das Datum des letzten Tages angezeigt, für den eine Zahl vorliegt."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7-Tage-Mittelwert"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Mittelwert der vergangenen 7 Tage"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Gesamt"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Gesamtzahl seit Jahresbeginn 2020 bzw. Einführung der App (15. Juni 2020)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Trend"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"Die Pfeilrichtung zeigt an, ob der Trend nach oben oder nach unten geht oder relativ stabil ist, d.h. eine Abweichung von weniger als 1\% im Vortagesvergleich bzw. 5\% im Vorwochenvergleich aufweist. Die Farbe bewertet diesen Trend als positiv (grün), negativ (rot) oder neutral (grau). Der Trend vergleicht den Wert vom Vortag mit dem Wert von vor zwei Tagen bzw. für die 7-Tage-Trends den Mittelwert der letzten 7 Tage mit dem der vorausgegangenen 7 Tage."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"Folgende Trends können angezeigt werden:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Trend steigend"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Trend fallend"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Trend stabil"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"Die Bewertung des Trends bei den warnenden Personen ändert sich je nach Infektionslage. Deshalb wird dieser Trend immer neutral dargestellt."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/de/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"Abstrakte Darstellung eines Smartphones mit vier Platzhaltern für Informationen"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1311,6 +1403,9 @@ <string name="errors_risk_detection_limit_reached_title">"Limit bereits erreicht"</string> <!-- XTXT: error dialog - Error description when the provideDiagnosisKeys quota limit was reached. --> <string name="errors_risk_detection_limit_reached_description">"Heute sind keine weiteren Risiko-Ãœberprüfungen möglich, weil das von Ihrem Betriebssystem festgelegte Limit von Risiko-Ãœberprüfungen pro Tag bereits erreicht ist. Bitte überprüfen Sie Ihren Risikostatus morgen wieder."</string> + <!-- XTXT: error dialog - Error description when the ssl certificate is deactivated on the device. --> + <string name="errors_ssl_certificate_deactivated">"Bitte aktivieren Sie das SYSTEM Sicherheitszertifikat T-Systems Enterprise Services GmbH, T-TeleSec GlobalRoot Class 2 auf Ihrem Gerät. Mehr Informationen finden Sie in den FAQs auf https://coronawarn.app unter „URSACHE 2001“."</string> + <!-- #################################### Generic Error Messages ###################################### --> @@ -1499,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">Abbrechen</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"Weitere Informationen"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">7-Tage-R-Wert</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">Aktuell</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">durchschnittliche Ansteckungen pro infizierter Person</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">Trend steigend</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">Trend fallend</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">Trend stabil</string> + </resources> diff --git a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml index 85a427e695a6232c58da6cf2050a352189a5ce51..94c4c3a7620bc0f25a6e5b79d422560146c240c7 100644 --- a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml @@ -110,6 +110,8 @@ <string name="accessibility_location_selected">"Place %s is selected"</string> <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"Person %s is selected"</string> + <!-- XTXT: Day View headline (description for screen readers) --> + <string name="accessibility_day_view_header">"Day View"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Select"</string> 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 7cbadff14d75208b01a971cc6eb36986537091df..4c74448a27b23ca34f8165ddc759838e3b2339e1 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -1273,6 +1273,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Detailed Information on Data Processing and Your Consent"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7-Day Incidence"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"confirmed new infections per 100,000 residents"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Confirmed New Infections"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"7-Day Average"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Total"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Warnings by App Users"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"About Corona-Warn-App"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Today"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Yesterday"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Up to today"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Up to yesterday"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Currently"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"Up to %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"million"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Key Figures"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"Explanation of Statistics"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Confirmed New Infections"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Number of persons who have been diagnosed with coronavirus and reported to the RKI"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Warnings by App Users"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Number of app users who have been diagnosed with coronavirus and have warned others through the app"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7-Day Incidence"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Total number of confirmed new infections in the last 7 days (by reporting date) per 100,000 residents"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7-Day R Value"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"The reproduction figure, R, indicates how many people an average infected person has infected further. The current value takes data up to the last 5 days into account.\n\nFor more information, refer to the\nFAQ for the statistics."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"FAQ for the statistics."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Legend"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Time Period"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Yesterday/Up to yesterday"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Number for the previous day/up to the previous day. If no figures are available yet for the previous day, the last date for which figures are available is displayed."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7-Day Average"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Average value from the past 7 days"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Total"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Total number since the start of the year or launch of the app (June 15, 2020)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Trend"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"The arrow direction indicates whether the trend is increasing, decreasing, or remaining steady – that is, demonstrates a deviation of less than 1\% compared to the previous day or 5\% compared to the previous week. The color indicates this trend as positive (green), negative (red), or neutral (gray). The trend compares the value from the previous day with the value from two days ago or, for the 7-day trends, the average value from the last 7 days with the average value from the 7 days prior to that."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"The following trends can be shown:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Trend: Upwards"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Trend: Downwards"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Trend: Steady"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"The assessment of the trend for warnings by app users changes depending on current infection levels, which is why this trend is always displayed as neutral."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"Abstract picture of a smartphone with four placeholders for information"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1460,7 +1558,7 @@ <!-- XLNK: Terms of use link inside delta interoperability screen--> <string name="interoperability_onboarding_delta_terms_link">"Display terms of use"</string> <!-- XTXT: Description of the expanded terms in delta interopoerability screen part 2 --> - <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"The Terms of Use and privacy notice can also be found in the menu under “App Information†and in the app description in your app store. The changes will not affect how you use the app. If you continue to use the app or reopen it, we will assume that you agree to the updated Terms of Use."</string> + <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"The terms of use and privacy notice can also be found in the menu under “App Information†and in the app description in your app store. The changes will not affect how you use the app. If you continue to use the app or reopen it, we will assume that you agree to the updated terms of use."</string> <!-- XHED: Header of the delta onboarding screen for interoperability. If the user opens the app for the first time after the interoperability update --> <string name="interoperability_onboarding_delta_title">"Transnational\nExposure Logging"</string> <!-- XTXT: Description of the interoperability extension of the app. Below interoperability_onboarding_delta_title --> @@ -1496,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Cancel"</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"Further information"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"7-Day R Value"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Currently"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"average infections by each infected person"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"Trend: Upwards"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"Trend: Downwards"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"Trend: Steady"</string> + </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-night/colors.xml b/Corona-Warn-App/src/main/res/values-night/colors.xml index ebaf2624064091472d2aef21eb07de19b288f9c6..b570534002d8ba3a74c8d288bb59aff9058352d8 100644 --- a/Corona-Warn-App/src/main/res/values-night/colors.xml +++ b/Corona-Warn-App/src/main/res/values-night/colors.xml @@ -65,4 +65,7 @@ <color name="colorContactDiaryListItemPressed">#39393A</color> <color name="colorContactDiaryBackground">#232324</color> + <!-- Statistics --> + <color name="colorStatisticsValueLabel">#A7A7A7</color> + <color name="colorStatisticsPrimaryValue">#FFFFFF</color> </resources> diff --git a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml index afb1d66658fd048fc28d7329969d2fb77a019e75..91371b37d2c78106bb39d1635abc64f2828344a6 100644 --- a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml @@ -110,6 +110,8 @@ <string name="accessibility_location_selected">"Miejsce %s nie zostaÅ‚o wybrane"</string> <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"Osoba %s nie zostaÅ‚a wybrana"</string> + <!-- XTXT: Day View headline (description for screen readers) --> + <string name="accessibility_day_view_header">"WglÄ…d dzienny"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Wybierz"</string> diff --git a/Corona-Warn-App/src/main/res/values-pl/strings.xml b/Corona-Warn-App/src/main/res/values-pl/strings.xml index 5cf6b8abec41a7976df7698731f58c602a70331c..8a3d90fa530918ac48c14c9f8c041473802cdd67 100644 --- a/Corona-Warn-App/src/main/res/values-pl/strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/strings.xml @@ -158,8 +158,8 @@ <plurals name="risk_card_low_risk_encounter_days_body"> <item quantity="one">"Narażenia z niskim ryzykiem w ciÄ…gu jednego dnia"</item> <item quantity="other">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dnia"</item> - <item quantity="zero">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dni"</item> - <item quantity="two">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dni"</item> + <item quantity="zero">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dnia"</item> + <item quantity="two">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dnia"</item> <item quantity="few">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dni"</item> <item quantity="many">"Narażenia z niskim ryzykiem w ciÄ…gu %1$d dni"</item> </plurals> @@ -168,8 +168,8 @@ <plurals name="risk_card_high_risk_encounter_days_body"> <item quantity="one">"Narażenia w ciÄ…gu jednego dnia z podwyższonym ryzykiem"</item> <item quantity="other">"Narażenia w ciÄ…gu %1$d dnia z podwyższonym ryzykiem"</item> - <item quantity="zero">"Narażenia w ciÄ…gu %1$d dni z podwyższonym ryzykiem"</item> - <item quantity="two">"Narażenia w ciÄ…gu %1$d dni z podwyższonym ryzykiem"</item> + <item quantity="zero">"Narażenia w ciÄ…gu %1$d dnia z podwyższonym ryzykiem"</item> + <item quantity="two">"Narażenia w ciÄ…gu %1$d dnia z podwyższonym ryzykiem"</item> <item quantity="few">"Narażenia w ciÄ…gu %1$d dni z podwyższonym ryzykiem"</item> <item quantity="many">"Narażenia w ciÄ…gu %1$d dni z podwyższonym ryzykiem"</item> </plurals> @@ -332,8 +332,8 @@ <plurals name="risk_details_information_body_increased_risk"> <item quantity="one">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dzieÅ„ temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> <item quantity="other">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dnia temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> - <item quantity="zero">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dni temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> - <item quantity="two">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dni temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> + <item quantity="zero">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dnia temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> + <item quantity="two">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dnia temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> <item quantity="few">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dni temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> <item quantity="many">"Masz podwyższone ryzyko zakażenia, ponieważ %1$s dni temu byÅ‚eÅ›(-aÅ›) narażony(-a) na dÅ‚uższy, bliski kontakt z co najmniej jednÄ… osobÄ…, u której zdiagnozowano koronawirusa."</item> </plurals> @@ -562,8 +562,8 @@ <plurals name="settings_tracing_status_body_active"> <item quantity="one">"Rejestrowanie narażenia jest aktywne od jednego dnia.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> <item quantity="other">"Rejestrowanie narażenia jest aktywne od %1$s dnia.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> - <item quantity="zero">"Rejestrowanie narażenia jest aktywne od %1$s dni.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> - <item quantity="two">"Rejestrowanie narażenia jest aktywne od %1$s dni.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> + <item quantity="zero">"Rejestrowanie narażenia jest aktywne od %1$s dnia.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> + <item quantity="two">"Rejestrowanie narażenia jest aktywne od %1$s dnia.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> <item quantity="few">"Rejestrowanie narażenia jest aktywne od %1$s dni.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> <item quantity="many">"Rejestrowanie narażenia jest aktywne od %1$s dni.\nSprawdzanie narażeÅ„ jest wiarygodne tylko wtedy, gdy rejestrowanie narażenia jest aktywowane na staÅ‚e."</item> </plurals> @@ -1273,6 +1273,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Szczegółowe informacje na temat przetwarzania danych i Twojej zgodzie"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7-dniowa zapadalność"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"potwierdzone nowe zakażenia na 100 000 mieszkaÅ„ców"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Potwierdzone nowe zakażenia"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"7-dniowa Å›rednia"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Suma"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Ostrzeżenia od użytkowników aplikacji"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"Informacje o aplikacji Corona-Warn-App"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Dzisiaj"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Wczoraj"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Do dzisiaj"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Do wczoraj"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Aktualnie"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"Do %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"mln"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Wskaźniki"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"ObjaÅ›nienie statystyk"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Potwierdzone nowe zakażenia"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Liczba osób ze zdiagnozowanym zakażeniem koronawirusem zgÅ‚oszonych do RKI"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Ostrzeżenia od użytkowników aplikacji"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Liczba użytkowników aplikacji ze zdiagnozowanym zakażeniem koronawirusem, którzy ostrzegli innych za pomocÄ… aplikacji"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7-dniowa zapadalność"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"ÅÄ…czna liczba potwierdzonych nowych zakażeÅ„ w ciÄ…gu ostatnich 7 dni (wedÅ‚ug daty zgÅ‚oszenia) na 100 000 mieszkaÅ„ców"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7-dniowa wartość R"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"Współczynnik reprodukcji R wskazuje liczbÄ™ osób, którÄ… zaraża przeciÄ™tna osoba zakażona. Bieżąca wartość uwzglÄ™dnia dane z ostatnich 5 dni.\n\nWiÄ™cej informacji znajdziesz w\nodpowiedziach na czÄ™sto zadawane pytania dotyczÄ…ce statystyk."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"CzÄ™sto zadawane pytania dotyczÄ…ce statystyk."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Legenda"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Okres"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Wczoraj/do wczoraj"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Liczba dotyczÄ…ca dnia poprzedniego/okresu do dnia poprzedniego. JeÅ›li dane liczbowe za poprzedni dzieÅ„ nie sÄ… jeszcze dostÄ™pne, wyÅ›wietlana jest ostatnia data, dla której istniejÄ… dane."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7-dniowa Å›rednia"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Åšrednia wartość z ostatnich 7 dni"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Suma"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"ÅÄ…czna liczba od poczÄ…tku roku lub wprowadzenia aplikacji na rynek (15 czerwca 2020 r.)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Trend"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"Kierunek strzaÅ‚ki wskazuje, czy trend roÅ›nie, maleje, czy też pozostaje stabilny, czyli wykazuje odchylenie mniejsze niż 1\% w porównaniu z poprzednim dniem lub 5\% w porównaniu z poprzednim tygodniem. Kolor wskazuje, że trend jest pozytywny (zielony), negatywny (czerwony) lub neutralny (szary). Trend ustala siÄ™ poprzez porównanie wartoÅ›ci z poprzedniego dnia z wartoÅ›ciÄ… sprzed dwóch dni. W przypadku trendów 7-dniowych porównywana jest Å›rednia wartość z ostatnich 7 dni ze Å›redniÄ… wartoÅ›ciÄ… z 7 dni je poprzedzajÄ…cych."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"Można przedstawić nastÄ™pujÄ…ce trendy:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Trend: RosnÄ…cy"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Trend: MalejÄ…cy"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Trend: Stabilny"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"Ocena trendu dotyczÄ…cego ostrzeżeÅ„ wysyÅ‚anych przez użytkowników aplikacji zmienia siÄ™ w zależnoÅ›ci od aktualnych poziomów zakażenia, dlatego trend ten jest zawsze wyÅ›wietlany jako neutralny."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"Abstrakcyjne zdjÄ™cie smartfona z czterema symbolami zastÄ™pczymi do celów informacyjnych"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1460,7 +1558,7 @@ <!-- XLNK: Terms of use link inside delta interoperability screen--> <string name="interoperability_onboarding_delta_terms_link">"WyÅ›wietl warunki korzystania"</string> <!-- XTXT: Description of the expanded terms in delta interopoerability screen part 2 --> - <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"Warunki korzystania i oÅ›wiadczenie o ochronie prywatnoÅ›ci znajdujÄ… siÄ™ w menu w sekcji „Informacje o aplikacji†oraz w opisie aplikacji w Twoim sklepie z aplikacjami. Zmiany nie bÄ™dÄ… miaÅ‚y wpÅ‚ywu na korzystanie przez Ciebie z aplikacji. Dalsze korzystanie z aplikacji lub jej ponowne otwarcie bÄ™dzie oznaczać akceptacjÄ™ zaktualizowanych Warunków korzystania."</string> + <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"Warunki korzystania i oÅ›wiadczenie o ochronie prywatnoÅ›ci znajdujÄ… siÄ™ w menu w sekcji „Informacje o aplikacji†oraz w opisie aplikacji w Twoim sklepie z aplikacjami. Zmiany nie bÄ™dÄ… miaÅ‚y wpÅ‚ywu na korzystanie przez Ciebie z aplikacji. Dalsze korzystanie z aplikacji lub jej ponowne otwarcie bÄ™dzie oznaczać akceptacjÄ™ zaktualizowanych warunków korzystania."</string> <!-- XHED: Header of the delta onboarding screen for interoperability. If the user opens the app for the first time after the interoperability update --> <string name="interoperability_onboarding_delta_title">"Rejestrowanie narażenia\nw różnych krajach"</string> <!-- XTXT: Description of the interoperability extension of the app. Below interoperability_onboarding_delta_title --> @@ -1496,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Anuluj"</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"WiÄ™cej informacji"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"7-dniowa wartość R"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Aktualnie"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"Å›rednia liczba zakażeÅ„ przez zakażonÄ… osobÄ™"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"Trend: RosnÄ…cy"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"Trend: MalejÄ…cy"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"Trend: Stabilny"</string> + </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml index 40d5f5cc38b0d760c1efbc52aca41dbd0db676c0..ba3dd2f70a3710a70e785e7620a6cf3d3895a238 100644 --- a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml @@ -110,6 +110,8 @@ <string name="accessibility_location_selected">"Locul %s este selectat"</string> <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"Persoana %s este selectată"</string> + <!-- XTXT: Day View headline (description for screen readers) --> + <string name="accessibility_day_view_header">"Imagine zi"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Selectare"</string> diff --git a/Corona-Warn-App/src/main/res/values-ro/strings.xml b/Corona-Warn-App/src/main/res/values-ro/strings.xml index b839f59ddb3ace451a96ea9b6e70a9467b7acbbb..7876e5a6f1617caf7f18e36189deec8cd75ccd35 100644 --- a/Corona-Warn-App/src/main/res/values-ro/strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/strings.xml @@ -158,20 +158,20 @@ <plurals name="risk_card_low_risk_encounter_days_body"> <item quantity="one">"Expuneri cu risc redus într-o zi"</item> <item quantity="other">"Expuneri cu risc redus în %1$d de zile"</item> - <item quantity="zero">"Expuneri cu risc redus în %1$d zile"</item> - <item quantity="two">"Expuneri cu risc redus în %1$d zile"</item> + <item quantity="zero">"Expuneri cu risc redus în %1$d de zile"</item> + <item quantity="two">"Expuneri cu risc redus în %1$d de zile"</item> <item quantity="few">"Expuneri cu risc redus în %1$d zile"</item> - <item quantity="many">"Expuneri cu risc redus în %1$d zile"</item> + <item quantity="many">"Expuneri cu risc redus în %1$d de zile"</item> </plurals> <!-- XTXT: risk card - High risk state - Days with high risk encounters --> <plurals name="risk_card_high_risk_encounter_days_body"> <item quantity="one">"Expuneri cu risc crescut într-o zi"</item> <item quantity="other">"Expuneri cu risc crescut în %1$d de zile"</item> - <item quantity="zero">"Expuneri cu risc crescut în %1$d zile"</item> - <item quantity="two">"Expuneri cu risc crescut în %1$d zile"</item> + <item quantity="zero">"Expuneri cu risc crescut în %1$d de zile"</item> + <item quantity="two">"Expuneri cu risc crescut în %1$d de zile"</item> <item quantity="few">"Expuneri cu risc crescut în %1$d zile"</item> - <item quantity="many">"Expuneri cu risc crescut în %1$d zile"</item> + <item quantity="many">"Expuneri cu risc crescut în %1$d de zile"</item> </plurals> <!-- XTXT: risk card - High risk state - Most recent date with high risk --> <string name="risk_card_high_risk_most_recent_body">"Cel mai recent pe %1$s"</string> @@ -332,10 +332,10 @@ <plurals name="risk_details_information_body_increased_risk"> <item quantity="one">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s zi pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> <item quantity="other">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s de zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> - <item quantity="zero">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> - <item quantity="two">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> + <item quantity="zero">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s de zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> + <item quantity="two">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s de zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> <item quantity="few">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> - <item quantity="many">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> + <item quantity="many">"AveÈ›i un risc crescut de infectare deoarece aÈ›i fost expus ultima dată acum %1$s de zile pe o perioadă mai lungă de timp È™i în strânsă proximitate cu cel puÈ›in o persoană diagnosticată cu coronavirus."</item> </plurals> <!-- YTXT: risk details - risk calculation explanation --> <string name="risk_details_information_body_notice">"Riscul dvs. de infectare este calculat pe baza datelor de înregistrare în jurnal a expunerilor (durata È™i proximitatea) la nivel local pe smartphone-ul dvs. Riscul dvs. de infectare nu poate fi văzut de o altă persoană sau transmis unei alte persoane."</string> @@ -562,10 +562,10 @@ <plurals name="settings_tracing_status_body_active"> <item quantity="one">"ÃŽnregistrarea în jurnal a expunerilor a fost activă o zi.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> <item quantity="other">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s de zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> - <item quantity="zero">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> - <item quantity="two">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> + <item quantity="zero">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s de zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> + <item quantity="two">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s de zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> <item quantity="few">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> - <item quantity="many">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> + <item quantity="many">"ÃŽnregistrarea în jurnal a expunerilor a fost activă %1$s de zile.\nO verificare a expunerii poate fi de încredere doar dacă înregistrarea în jurnal a expunerilor este activată permanent."</item> </plurals> <!-- XACT: settings(tracing) - describes illustration --> <string name="settings_tracing_illustration_description_active">"Trei persoane È™i-au activat pe smartphone înregistrarea în jurnal a expunerilor, ceea ce va duce la înregistrarea întâlnirilor lor."</string> @@ -1273,6 +1273,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"InformaÈ›ii detaliate privind Prelucrarea datelor È™i Consimțământul dvs."</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"IncidenÈ›a pe 7 zile"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"infecÈ›ii noi confirmate la 100.000 de locuitori"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"InfecÈ›ii noi confirmate"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"Media pe 7 zile"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Total"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Avertizări de la utilizatorii aplicaÈ›iei"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"Despre aplicaÈ›ia Corona-Warn"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Astăzi"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Ieri"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Până astăzi"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Până ieri"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Curentă"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"Până %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"milion(e)"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Indicatori"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"Explicarea statisticii"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"InfecÈ›ii noi confirmate"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Numărul de persoane care au fost diagnosticate cu coronavirus È™i raportate la RKI"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Avertizări de la utilizatorii aplicaÈ›iei"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Numărul de utilizatori ai aplicaÈ›iei care au fost diagnosticaÈ›i cu coronavirus È™i care i-au avertizat pe ceilalÈ›i prin aplicaÈ›ie"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"IncidenÈ›a pe 7 zile"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Numărul total de infecÈ›ii noi confirmate în ultimele 7 zile (după data de raportare) la 100.000 de locuitori"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"Valoarea R pe 7 zile"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"Numărul de reproducÈ›ie R indică câte persoane a infectat mai departe, în medie, o persoană infectată. Valoarea curentă ia în considerare cel mult ultimele 5 zile.\n\nPentru alte informaÈ›ii, consultaÈ›i\nîntrebările frecvente despre statistică."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"ÃŽntrebări frecvente despre statistică."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Legendă"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Perioadă de timp"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Ieri/Până ieri"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Numărul pentru ziua anterioară/până la ziua anterioară. Dacă încă nu sunt disponibile cifrele pentru ziua anterioară, este afiÈ™ată ultima dată pentru care sunt disponibile cifrele."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"Media pe 7 zile"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Valoarea medie din ultimele 7 zile"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Total"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Numărul total de la începutul anului sau de la lansarea aplicaÈ›iei (15 iunie 2020)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Tendință"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"DirecÈ›ia săgeÈ›ii indică dacă tendinÈ›a este în creÈ™tere, în scădere sau dacă rămâne constantă – mai exact, demonstrează o abatere de sub 1\% în comparaÈ›ie cu ziua precedentă sau de 5\% în comparaÈ›ie cu săptămâna precedentă. Culoarea indică faptul că tendinÈ›a este pozitivă (verde), negativă (roÈ™u) sau neutră (gri). TendinÈ›a compară valoarea din ziua precedentă cu valoarea de acum două zile sau, pentru tendinÈ›ele pe 7 zile, valoarea medie din ultimele 7 zile cu valoarea medie din ultimele 7 zile anterioare acesteia."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"Pot fi afiÈ™ate următoarele tendinÈ›e:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Tendință: ascendentă"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Tendință: descendentă"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Tendință: constantă"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"Evaluarea tendinÈ›ei pentru avertizările de către utilizatorii aplicaÈ›iei se modifică în funcÈ›ie de nivelul de infectare curent, de aceea această tendință este afiÈ™ată întotdeauna ca neutră."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"O imagine abstractă a unui smartphone cu patru substituenÈ›i pentru informaÈ›ii"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1460,7 +1558,7 @@ <!-- XLNK: Terms of use link inside delta interoperability screen--> <string name="interoperability_onboarding_delta_terms_link">"AfiÈ™are condiÈ›ii de utilizare"</string> <!-- XTXT: Description of the expanded terms in delta interopoerability screen part 2 --> - <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"CondiÈ›iile de utilizare È™i înÈ™tiinÈ›area privind confidenÈ›ialitatea pot fi găsite, de asemenea, în meniu la „InformaÈ›ii aplicaÈ›ieâ€, dar È™i în descrierea aplicaÈ›iei din App Store. Modificările nu vor afecta modul în care utilizaÈ›i aplicaÈ›ia. Dacă veÈ›i continua să utilizaÈ›i aplicaÈ›ia sau dacă o redeschideÈ›i, vom presupune că sunteÈ›i de acord cu CondiÈ›iile de utilizare actualizate."</string> + <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"CondiÈ›iile de utilizare È™i înÈ™tiinÈ›area privind confidenÈ›ialitatea pot fi găsite, de asemenea, în meniu la „InformaÈ›ii aplicaÈ›ieâ€, dar È™i în descrierea aplicaÈ›iei din App Store. Modificările nu vor afecta modul în care utilizaÈ›i aplicaÈ›ia. Dacă veÈ›i continua să utilizaÈ›i aplicaÈ›ia sau dacă o redeschideÈ›i, vom presupune că sunteÈ›i de acord cu condiÈ›iile de utilizare actualizate."</string> <!-- XHED: Header of the delta onboarding screen for interoperability. If the user opens the app for the first time after the interoperability update --> <string name="interoperability_onboarding_delta_title">"ÃŽnregistrare transnaÈ›ională\na expunerilor în jurnal"</string> <!-- XTXT: Description of the interoperability extension of the app. Below interoperability_onboarding_delta_title --> @@ -1496,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Anulare"</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"InformaÈ›ii suplimentare"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"Valoarea R pe 7 zile"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Curentă"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"infecÈ›ii medii de la fiecare persoană infectată"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"Tendință: ascendentă"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"Tendință: descendentă"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"Tendință: constantă"</string> + </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml index e28ec5ff585e0dc7a6bfebfae49b904bee88fe00..2dc0545f0cfd9733b84ec131e7f66f2524a6a3a4 100644 --- a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml @@ -110,6 +110,8 @@ <string name="accessibility_location_selected">"Yer %s seçildi"</string> <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"KiÅŸi %s seçildi"</string> + <!-- XTXT: Day View headline (description for screen readers) --> + <string name="accessibility_day_view_header">"Gün Görünümü"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Seç"</string> diff --git a/Corona-Warn-App/src/main/res/values-tr/strings.xml b/Corona-Warn-App/src/main/res/values-tr/strings.xml index bb52e0ab62afb17c9246082c12c8e71ee800b10d..2faead212057b524fa5761198c9cf886411d3028 100644 --- a/Corona-Warn-App/src/main/res/values-tr/strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/strings.xml @@ -1273,6 +1273,104 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Veri Ä°ÅŸleme ve Onayınız Hakkında Ayrıntılı Bilgiler"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7 Günlük Olay"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"her 100.000 vatandaÅŸta onaylanan yeni enfeksiyon sayısı"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Onaylanan Yeni Enfeksiyon Sayısı"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"7 Günlük Ortalama"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Toplam"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Uygulama Kullanıcılarının Uyarıları"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"Corona-Warn-App Hakkında"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Bugün"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Dün"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Bugüne kadar"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Düne kadar"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Åžu Anda"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"%s tarihine kadar"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"milyon"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Önemli DeÄŸerler"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"Ä°statistiklerin Açıklaması"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Onaylanan Yeni Enfeksiyon Sayısı"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Koronavirüs tanısı koyulan ve RKI’ye bildirilen kiÅŸi sayısı"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Uygulama Kullanıcılarının Uyarıları"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Koronavirüs tanısı koyulan ve uygulama üzerinden diÄŸer kullanıcıları uyaran uygulama kullanıcılarının sayısı"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7 Günlük Olay"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Her 100.000 vatandaÅŸta son 7 günde (bildirme tarihine göre) onaylanan toplam yeni enfeksiyon sayısı"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7 Günlük R DeÄŸeri"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"Reprodüksiyon deÄŸeri R, enfekte olan bir kiÅŸinin ortalama olarak kaç kiÅŸiyi enfekte ettiÄŸini belirtir. Geçerli deÄŸerde, en fazla son 5 günün verileri dikkate alınır.\n\nDaha fazla bilgi için\nistatistiklere iliÅŸkin SSS bölümüne bakın."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"SSS bölümüne bakın."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Açıklama"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Dönem"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Dün/Düne kadar"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Önceki güne ait/önceki güne kadar olan sayıdır. Önceki güne ait hiçbir deÄŸer yoksa deÄŸer olan son güne ait veriler görüntülenir."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7 Günlük Ortalama"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Son 7 güne ait ortalama deÄŸer"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Toplam"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Yıl başından ya da uygulamanın kullanıma sunulmasından itibaren toplam sayı (15 Haziran 2020)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Trend"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"Ok yönü trendin arttığını, azaldığını ya da sabit kaldığını belirtir. Sabit kalması, önceki güne göre %1’den az veya önceki haftaya göre %5’ten az sapma olduÄŸu anlamına gelir. Renk ise bu trendin pozitif (yeÅŸil), negatif (kırmızı) ya da nötr (gri) olduÄŸunu belirtir. Trend için, önceki güne ait deÄŸer iki gün önceki deÄŸerle karşılaÅŸtırılır ya da 7 günlük trendler için son 7 güne ait ortalama deÄŸer ondan önceki 7 güne ait ortalama deÄŸerle karşılaÅŸtırılır."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"Åžu trendler gösterilebilir:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Trend: Yukarı"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Trend: AÅŸağı"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Trend: Sabit"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"Uygulama kullanıcıları tarafından yapılan uyarılara iliÅŸkin trend deÄŸerlendirmesi geçerli enfeksiyon seviyesine göre deÄŸiÅŸiklik gösterir. Bu nedenle trend her zaman nötr olarak görüntülenir."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"Bilgi için dört yer tutucu bulunan bir akıllı telefonun soyut resmi"</string> + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1460,7 +1558,7 @@ <!-- XLNK: Terms of use link inside delta interoperability screen--> <string name="interoperability_onboarding_delta_terms_link">"Kullanım koÅŸullarını görüntüle"</string> <!-- XTXT: Description of the expanded terms in delta interopoerability screen part 2 --> - <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"Kullanım KoÅŸulları ve gizlilik bildirimi \"Uygulama Bilgileri\" baÅŸlığındaki menüde ve uygulama maÄŸazanızdaki uygulama açıklamasında yer almaktadır. DeÄŸiÅŸiklikler, uygulamayı nasıl kullandığınızı etkilemeyecektir. Uygulamayı kullanmaya devam eder veya uygulamayı yeniden açarsanız güncellenen Kullanım KoÅŸullarını kabul ettiÄŸinizi varsayacağız."</string> + <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"Kullanım koÅŸulları ve gizlilik bildirimi \"Uygulama Bilgileri\" baÅŸlığındaki menüde ve uygulama maÄŸazanızdaki uygulama açıklamasında yer almaktadır. DeÄŸiÅŸiklikler, uygulamayı nasıl kullandığınızı etkilemeyecektir. Uygulamayı kullanmaya devam eder veya uygulamayı yeniden açarsanız güncellenen kullanım koÅŸullarını kabul ettiÄŸinizi varsayacağız."</string> <!-- XHED: Header of the delta onboarding screen for interoperability. If the user opens the app for the first time after the interoperability update --> <string name="interoperability_onboarding_delta_title">"Ãœlkeler Arası\nMaruz Kalma Günlüğü"</string> <!-- XTXT: Description of the interoperability extension of the app. Below interoperability_onboarding_delta_title --> @@ -1496,4 +1594,20 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Ä°ptal"</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"Daha fazla bilgi"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"7 Günlük R DeÄŸeri"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Åžu Anda"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"enfekte olan kiÅŸi başına ortalama enfeksiyon sayısı"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"Trend: Yukarı"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"Trend: AÅŸağı"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"Trend: Sabit"</string> + </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/colors.xml b/Corona-Warn-App/src/main/res/values/colors.xml index 114cfa9ba9ef36db9d6efd703719fe4e121792e2..959ba9df9f6eb8ddb65d1e27e7e2c93cfcd378c0 100644 --- a/Corona-Warn-App/src/main/res/values/colors.xml +++ b/Corona-Warn-App/src/main/res/values/colors.xml @@ -78,4 +78,10 @@ <color name="colorContactDiaryListItemPressed">#D7D7D7</color> <color name="colorContactDiaryBackground">#FFFFFF</color> + <!-- Statistics --> + <color name="colorStatisticsValueLabel">#747576</color> + <color name="colorStatisticsPrimaryValue">#000000</color> + <color name="colorStatisticsTrendPositive">#2E854B</color> + <color name="colorStatisticsTrendNegative">#C11633</color> + <color name="colorStatisticsTrendNeutral">#5D6F80</color> </resources> diff --git a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml index 6671b37dd501cb58321163fd7089d9461e146e3a..4235e7cffd13bcad00397ef5e732f6d8d7d06d58 100644 --- a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml @@ -41,6 +41,8 @@ <string name="contact_diary_onboarding_functionality_fourth_section">"You can remove entries for people and places from your journal at any time. Journal entries will be deleted automatically after 16 days."</string> <!-- XTXT: Contact diary onboarding screen fifth functionality --> <string name="contact_diary_onboarding_functionality_fifth_section">"You can export your contact journal in plain text format and then print out the entries, edit them, or provide them to your public health authority as needed."</string> + <!-- XTXT: Contact diary onboarding screen sixth functionality --> + <string name="contact_diary_onboarding_functionality_sixth_section"></string> <!-- XTXT: Title for the contact diary onboarding screen --> <string name="contact_diary_title">"Contact Journal"</string> <!-- XTXT: Body for legal information of the contact diary onboarding screen --> @@ -130,7 +132,7 @@ <!-- XTXT: person is selected (description for screen readers) --> <string name="accessibility_person_selected">"Person %s is selected"</string> <!-- XTXT: Day View headline (description for screen readers) --> - <string name="accessibility_day_view_header"></string> + <string name="accessibility_day_view_header">"Day View"</string> <!-- XTXT: Select (description for screen readers) --> <string name="accessibility_action_select">"Select"</string> diff --git a/Corona-Warn-App/src/main/res/values/dimens.xml b/Corona-Warn-App/src/main/res/values/dimens.xml index 46b3eb9ebca42d7218f4d5c6487ef9d96dc6e035..833899f89788127455e7b39654c57c05e1610e59 100644 --- a/Corona-Warn-App/src/main/res/values/dimens.xml +++ b/Corona-Warn-App/src/main/res/values/dimens.xml @@ -34,6 +34,7 @@ <!-- default spacing for guidelines --> <dimen name="guideline_card">12dp</dimen> + <dimen name="guideline_statistics_card_content">96dp</dimen> <dimen name="guideline_start">@dimen/spacing_normal</dimen> <dimen name="guideline_end">@dimen/spacing_normal</dimen> <dimen name="guideline_top">@dimen/spacing_normal</dimen> @@ -61,6 +62,7 @@ <!-- general --> <dimen name="radius_button">@dimen/spacing_tiny</dimen> <dimen name="radius_card">4dp</dimen> + <dimen name="radius_info_button">@dimen/spacing_huge</dimen> <dimen name="icon_size">20dp</dimen> <dimen name="icon_size_risk_card">40dp</dimen> <dimen name="icon_size_main_card">40dp</dimen> @@ -82,6 +84,9 @@ <dimen name="circle_icon_big_padding">8dp</dimen> <dimen name="behavior_bullet_point_size">16dp</dimen> + <dimen name="button_icon">48dp</dimen> + <dimen name="button_icon_padding">12dp</dimen> + <!-- todo illustration sizes --> <!-- Submission --> @@ -140,4 +145,6 @@ <!-- Contact Diary --> <dimen name="contact_diary_list_item">64dp</dimen> <dimen name="contact_diary_nested_recyclerview_margin">60dp</dimen> + <dimen name="statistics_card_height">328dp</dimen> + <dimen name="statistics_card_width">300dp</dimen> </resources> diff --git a/Corona-Warn-App/src/main/res/values/release_info_strings.xml b/Corona-Warn-App/src/main/res/values/release_info_strings.xml index c35773e1f72f1fe6181380b86bb138f42f7230b0..26a805a5940b3e14fcdafa5fff94087718427cde 100644 --- a/Corona-Warn-App/src/main/res/values/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values/release_info_strings.xml @@ -7,17 +7,17 @@ <!-- XHED: Title for the release info screen --> <string name="release_info_header"/> <!-- XHED: Version title for the release info screen --> - <string name="release_info_version_title">"Release <xliff:g id="release_app_version" example="1.12">"%1$d"</xliff:g></string> + <string name="release_info_version_title">"Release %1$d"</string> <!-- XTXT: Description for the release info screen --> <string name="release_info_version_body" /> <!-- XTXT: Encounter history title for the release info screen --> - <string name="release_info_encounter_history_title" /> + <string name="release_info_1_12_encounter_history_title" /> <!-- XTXT: Encounter history body for the release info screen --> - <string name="release_info_encounter_history_body" /> + <string name="release_info_1_12_encounter_history_body" /> <!-- XTXT: Statistics title for the release info screen --> - <string name="release_info_statistics_title" /> + <string name="release_info_1_12_statistics_title" /> <!-- XTXT: Statistics body for the release info screen --> - <string name="release_info_statistics_body" /> + <string name="release_info_1_12_statistics_body" /> <!-- XBUT: Continue button for the release info screen --> <string name="release_info_continue_button" /> diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 51eb92236818c0f2a5d6327a3a64afa1f9601a7c..fe5938c5b34089cbf6ecfa45bb1c256633e5b6ae 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -970,6 +970,8 @@ <!-- Submission Intro --> <!-- XBUT: Submission introduction next button--> <string name="submission_intro_button_next">"Next"</string> + <!-- YTXT: Description for illustration in submission onboarding--> + <string name="submission_intro_illustration_description"></string> <!-- Dispatcher --> <!-- XHED: Page headline for dispatcher menu --> @@ -1284,6 +1286,105 @@ <!-- XTXT: Your consent screen detailed information --> <string name="submission_your_consent_agreement_details">"Detailed Information on Data Processing and Your Consent"</string> + <!-- #################################### + Statistics + ###################################### --> + + <!-- Incidence statistics card --> + <!-- XHED: Title for incident statistics card --> + <string name="statistics_card_incidence_title">"7-Day Incidence"</string> + <!-- XTXT: Description in statistics card that is displayed below the incidence value--> + <string name="statistics_card_incidence_value_description">"confirmed new infections per 100,000 residents"</string> + + <!-- Infections statistics card --> + <!-- XHED: Title for infections statistics card --> + <string name="statistics_card_infections_title">"Confirmed New Infections"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the secondary value--> + <string name="statistics_card_infections_secondary_label">"7-Day Average"</string> + <!-- XTXT: Label in infections statistics card that is displayed above the tertiary value--> + <string name="statistics_card_infections_tertiary_label">"Total"</string> + + <!-- Incidence statistics card --> + <!-- XHED: Title for submission statistics card --> + <string name="statistics_card_submission_title">"Warnings by App Users"</string> + <!-- XTXT: Text displayed in the bottom of submission statistics card--> + <string name="statistics_card_submission_bottom_text">"About Corona-Warn-App"</string> + + <!-- XTXT: Timestamp refers to today's date in submission and infections cards --> + <string name="statistics_primary_value_today">"Today"</string> + <!-- XTXT: Timestamp refers to yesterday's date in submission and infections cards --> + <string name="statistics_primary_value_yesterday">"Yesterday"</string> + <!-- XTXT: Timestamp refers to today's date in incident card --> + <string name="statistics_primary_value_until_today">"Up to today"</string> + <!-- XTXT: Timestamp refers to yesterday's date in incident card --> + <string name="statistics_primary_value_until_yesterday">"Up to yesterday"</string> + <!-- XTXT: Timestamp refers to today's date in Reproduction Number card --> + <string name="statistics_primary_value_current">"Currently"</string> + <!-- XTXT: Until [date] refers to statistics timestamp in the past --> + <string name="statistics_primary_value_until">"Up to %s"</string> + + <!-- XTXT: Suffix for very large statistics values - Abbreviation for "Million" --> + <string name="statistics_value_suffix_million">"million"</string> + + <!-- Explanation screen --> + <!-- XHED: Explanation screen title --> + <string name="statistics_explanation_title">"Key Figures"</string> + <!-- XHED: Explanation screen subtitle --> + <string name="statistics_explanation_subtitle">"Explanation of Statistics"</string> + <!-- XHED: Explanation screen confirmed new infections title --> + <string name="statistics_explanation_confirmed_new_infection_title">"Confirmed New Infections"</string> + <!-- XTXT: Explanation screen confirmed new infections text --> + <string name="statistics_explanation_confirmed_new_infection_text">"Number of persons who have been diagnosed with coronavirus and reported to the RKI"</string> + <!-- XHED: Explanation screen warned persons title --> + <string name="statistics_explanation_warned_persons_title">"Warnings by App Users"</string> + <!-- XTXT: Explanation screen warned persons text --> + <string name="statistics_explanation_warned_persons_text">"Number of app users who have been diagnosed with coronavirus and have warned others through the app"</string> + <!-- XHED: Explanation screen seven day incidence title --> + <string name="statistics_explanation_seven_day_incidence_title">"7-Day Incidence"</string> + <!-- XTXT: Explanation screen seven day incidence text --> + <string name="statistics_explanation_seven_day_incidence_text">"Total number of confirmed new infections in the last 7 days (by reporting date) per 100,000 residents"</string> + <!-- XHED: Explanation screen seven day r-value title --> + <string name="statistics_explanation_seven_day_r_value_title">"7-Day R Value"</string> + <!-- XTXT: Explanation screen seven day r-value text --> + <string name="statistics_explanation_seven_day_r_value_text">"The reproduction figure, R, indicates how many people an average infected person has infected further. The current value takes data up to the last 5 days into account.\n\nFor more information, refer to the\nFAQ for the statistics."</string> + <!-- XTXT: Explanation screen seven day r-value link label --> + <string name="statistics_explanation_seven_day_r_link_label">"FAQ for the statistics."</string> + <!-- XHED: Explanation screen legend title --> + <string name="statistics_explanation_legend_title">"Legend"</string> + <!-- XHED: Explanation screen period title --> + <string name="statistics_explanation_period_title">"Time Period"</string> + <!-- XHED: Explanation screen period yesterday subtitle --> + <string name="statistics_explanation_period_yesterday_subtitle">"Yesterday/Up to yesterday"</string> + <!-- XTXT: Explanation screen period yesterday text --> + <string name="statistics_explanation_period_yesterday_text">"Number for the previous day/up to the previous day. If no figures are available yet for the previous day, the last date for which figures are available is displayed."</string> + <!-- XHED: Explanation screen period seven day subtitle --> + <string name="statistics_explanation_period_seven_day_subtitle">"7-Day Average"</string> + <!-- XTXT: Explanation screen period seven day text --> + <string name="statistics_explanation_period_seven_day_text">"Average value from the past 7 days"</string> + <!-- XHED: Explanation screen period total subtitle --> + <string name="statistics_explanation_period_total_subtitle">"Total"</string> + <!-- XTXT: Explanation screen period total text --> + <string name="statistics_explanation_period_total_text">"Total number since the start of the year or launch of the app (June 15, 2020)"</string> + <!-- XHED: Explanation screen trend title --> + <string name="statistics_explanation_trend_title">"Trend"</string> + <!-- YTXT: Explanation screen trend text --> + <string name="statistics_explanation_trend_text">"The arrow direction indicates whether the trend is increasing, decreasing, or remaining steady – that is, demonstrates a deviation of less than 1\% compared to the previous day or 5\% compared to the previous week. The color indicates this trend as positive (green), negative (red), or neutral (gray). The trend compares the value from the previous day with the value from two days ago or, for the 7-day trends, the average value from the last 7 days with the average value from the 7 days prior to that."</string> + <!-- XHED: Explanation screen trend icons title --> + <string name="statistics_explanation_trend_icons_title">"The following trends can be shown:"</string> + <!-- XHED: Explanation screen trend increasing title --> + <string name="statistics_explanation_trend_increasing_title">"Trend: Upwards"</string> + <!-- XHED: Explanation screen trend decreasing title --> + <string name="statistics_explanation_trend_decreasing_title">"Trend: Downwards"</string> + <!-- XHED: Explanation screen trend stable title --> + <string name="statistics_explanation_trend_stable_title">"Trend: Steady"</string> + <!-- XHED: Explanation screen trend description --> + <string name="statistics_explanation_trend_description">"The assessment of the trend for warnings by app users changes depending on current infection levels, which is why this trend is always displayed as neutral."</string> + <!-- XTXT: Explains user about statistics: URL, has to be "translated" into english (relevant for all languages except german) - https://www.coronawarn.app/en/faq/#further_details --> + <string name="statistics_explanation_faq_url">"https://www.coronawarn.app/en/faq/#further_details"</string> + <!-- XACT: Statistics explanation illustration description --> + <string name="statistics_explanation_illustration_description">"Abstract picture of a smartphone with four placeholders for information"</string> + + <!-- #################################### Button Tooltips for Accessibility ###################################### --> @@ -1319,6 +1420,9 @@ <string name="errors_risk_detection_limit_reached_title">"Limit already reached"</string> <!-- XTXT: error dialog - Error description when the provideDiagnosisKeys quota limit was reached. --> <string name="errors_risk_detection_limit_reached_description">"No more exposure checks possible today, as you have reached the maximum number of checks per day defined by your operating system. Please check your risk status again tomorrow."</string> + <!-- XTXT: error dialog - Error description when the ssl certificate is deactivated on the device. --> + <string name="errors_ssl_certificate_deactivated">"Bitte aktivieren Sie das SYSTEM Sicherheitszertifikat T-Systems Enterprise Services GmbH, T-TeleSec GlobalRoot Class 2 auf Ihrem Gerät. Mehr Informationen finden Sie in den FAQs auf https://coronawarn.app unter „URSACHE 2001“."</string> + <!-- #################################### Generic Error Messages ###################################### --> @@ -1473,7 +1577,7 @@ <!-- XLNK: Terms of use link inside delta interoperability screen--> <string name="interoperability_onboarding_delta_terms_link">"Display terms of use"</string> <!-- XTXT: Description of the expanded terms in delta interopoerability screen part 2 --> - <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"The Terms of Use and privacy notice can also be found in the menu under “App Information†and in the app description in your app store. The changes will not affect how you use the app. If you continue to use the app or reopen it, we will assume that you agree to the updated Terms of Use."</string> + <string name="interoperability_onboarding_delta_expanded_terms_text_part_2">"The terms of use and privacy notice can also be found in the menu under “App Information†and in the app description in your app store. The changes will not affect how you use the app. If you continue to use the app or reopen it, we will assume that you agree to the updated terms of use."</string> <!-- XHED: Header of the delta onboarding screen for interoperability. If the user opens the app for the first time after the interoperability update --> <string name="interoperability_onboarding_delta_title">"Transnational\nExposure Logging"</string> <!-- XTXT: Description of the interoperability extension of the app. Below interoperability_onboarding_delta_title --> @@ -1513,4 +1617,20 @@ <string name="empty_string_to_avoid_toolbar_announcement" translatable="false">""</string> + <!-- XTXT: Statistics information (Accessibilty) --> + <string name="statistics_info_button">"Further information"</string> + <!-- XHED: Title for statistics reproduction card --> + <string name="statistics_title_reproduction">"7-Day R Value"</string> + <!-- XTXT: Label for statistics reproduction card --> + <string name="statistics_reproduction_title">"Currently"</string> + <!-- XTXT: Caption for statistics reproduction card --> + <string name="statistics_reproduction_average">"average infections by each infected person"</string> + + <!-- XTXT: Statistics trend increasing (Accessibilty) --> + <string name="statistics_trend_increasing">"Trend: Upwards"</string> + <!-- XTXT: Statistics trend decreasing (Accessibilty) --> + <string name="statistics_trend_decreasing">"Trend: Downwards"</string> + <!-- XTXT: Statistics trend stable (Accessibilty) --> + <string name="statistics_trend_stable">"Trend: Steady"</string> + </resources> diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index 3edf12e8673785ab0d1ff18c6b88102404e3d67b..0fa57df7b1beafeb08892ab97cbdb08337c39a1c 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -119,18 +119,18 @@ <item name="android:textColor">@color/button_text_color_emphasized</item> </style> - <style name="buttonReset" parent="@style/button"> + <style name="buttonReset" parent="button"> <item name="android:backgroundTint">@color/button_red</item> <item name="android:textColor">@color/button_text_color_emphasized</item> </style> - <style name="buttonLight" parent="@style/button"> + <style name="buttonLight" parent="button"> <item name="android:backgroundTint">@color/button_light</item> <item name="android:stateListAnimator">@null</item> </style> <style name="buttonIcon"> - <item name="android:background">@drawable/circle</item> + <item name="android:background">@drawable/circle_ripple</item> <item name="android:backgroundTint">@color/button_back</item> </style> @@ -144,7 +144,7 @@ <item name="android:paddingBottom">@dimen/spacing_tiny</item> </style> - <style name="switchBase" parent="@style/Widget.AppCompat.CompoundButton.Switch"> + <style name="switchBase" parent="Widget.AppCompat.CompoundButton.Switch"> <item name="android:colorControlActivated">@color/colorAccentTintIcon</item> </style> @@ -408,4 +408,33 @@ <item name="android:background">@drawable/contact_diary_card_ripple</item> </style> + <style name="StatisticsCardInfoButton" parent="buttonIcon"> + <item name="android:height">@dimen/button_icon</item> + <item name="android:width">@dimen/button_icon</item> + <item name="android:src">@drawable/ic_info</item> + <item name="android:padding">14dp</item> + <item name="android:contentDescription">@string/statistics_info_button</item> + </style> + + <style name="StatisticsCardPrimaryTitle" parent="headline5" /> + + <style name="StatisticsCardValueLabel" parent="subtitle"> + <item name="android:textColor">@color/colorStatisticsValueLabel</item> + </style> + + <style name="StatisticsCardTitleCaption"> + <item name="android:textColor">@color/colorStatisticsValueLabel</item> + <item name="android:textSize">@dimen/font_small</item> + </style> + + <style name="StatisticsCardPrimaryValue"> + <item name="android:textColor">@color/colorStatisticsPrimaryValue</item> + <item name="android:textSize">@dimen/font_huge</item> + </style> + + <style name="StatisticsCardSecondaryValue" parent="TextAppearance.AppCompat.Body2"> + <item name="android:textSize">@dimen/font_title</item> + <item name="android:textColor">@color/colorTextPrimary1</item> + </style> + </resources> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt index e5b901e0c00837569a2abf24179a8772019a3067..d919d3119653df4ddf2629442ded2bc4de5c5025 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt @@ -63,7 +63,7 @@ class AppConfigProviderTest : BaseIOTest() { private fun createInstance(scope: CoroutineScope) = AppConfigProvider( appConfigSource = appConfigSource, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), scope = scope ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSourceTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSourceTest.kt index 155d2f76c5a1293205cf55008a22d9a5308e5869..9bf5c9ddfcc08815f9ca89db789cc042ce3eaa75 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSourceTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSourceTest.kt @@ -68,7 +68,7 @@ class LocalAppConfigSourceTest : BaseIOTest() { private fun createInstance() = LocalAppConfigSource( storage = configStorage, parser = configParser, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/remote/RemoteAppConfigSourceTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/remote/RemoteAppConfigSourceTest.kt index 94de5fb5b9e6caa812eb90f456b3cb86f4ba9739..79f79fa9ef8f090edced1a5261976fffbaf9740b 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/remote/RemoteAppConfigSourceTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/sources/remote/RemoteAppConfigSourceTest.kt @@ -73,7 +73,7 @@ class RemoteAppConfigSourceTest : BaseIOTest() { server = configServer, storage = configStorage, parser = configParser, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTaskTest.kt index cee8408ed09c68521e4f2f0349e571a12bb24116..1b13ca0562f26bdbced0742f631daf01f25d1284 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryCleanTaskTest.kt @@ -25,6 +25,7 @@ class ContactDiaryCleanTaskTest : BaseTest() { coEvery { retentionCalculation.clearObsoleteContactDiaryPersonEncounters() } returns Unit coEvery { retentionCalculation.clearObsoleteContactDiaryLocationVisits() } returns Unit + coEvery { retentionCalculation.clearObsoleteRiskPerDate() } returns Unit } @AfterEach @@ -43,6 +44,7 @@ class ContactDiaryCleanTaskTest : BaseTest() { coVerifyOrder { retentionCalculation.clearObsoleteContactDiaryLocationVisits() retentionCalculation.clearObsoleteContactDiaryPersonEncounters() + retentionCalculation.clearObsoleteRiskPerDate() } result shouldNotBe null } @@ -54,7 +56,10 @@ class ContactDiaryCleanTaskTest : BaseTest() { val result = assertThrows<Exception> { createInstance().run(mockk()) } coVerify(exactly = 1) { retentionCalculation.clearObsoleteContactDiaryLocationVisits() } - coVerify(exactly = 0) { retentionCalculation.clearObsoleteContactDiaryPersonEncounters() } + coVerify(exactly = 0) { + retentionCalculation.clearObsoleteContactDiaryPersonEncounters() + retentionCalculation.clearObsoleteRiskPerDate() + } result shouldNotBe null } @@ -64,8 +69,27 @@ class ContactDiaryCleanTaskTest : BaseTest() { val result = assertThrows<Exception> { createInstance().run(mockk()) } - coVerify(exactly = 1) { retentionCalculation.clearObsoleteContactDiaryLocationVisits() } - coVerify(exactly = 1) { retentionCalculation.clearObsoleteContactDiaryPersonEncounters() } + coVerify(exactly = 1) { + retentionCalculation.clearObsoleteContactDiaryLocationVisits() + retentionCalculation.clearObsoleteContactDiaryPersonEncounters() + } + coVerify(exactly = 0) { retentionCalculation.clearObsoleteRiskPerDate() } + + result shouldNotBe null + } + + @Test + fun `risk per date fails`() = runBlockingTest { + coEvery { retentionCalculation.clearObsoleteRiskPerDate() } throws Exception() + + val result = assertThrows<Exception> { createInstance().run(mockk()) } + + coVerify(exactly = 1) { + retentionCalculation.clearObsoleteContactDiaryLocationVisits() + retentionCalculation.clearObsoleteContactDiaryPersonEncounters() + retentionCalculation.clearObsoleteRiskPerDate() + } + result shouldNotBe null } @@ -77,7 +101,10 @@ class ContactDiaryCleanTaskTest : BaseTest() { val result = assertThrows<Exception> { createInstance().run(mockk()) } coVerify(exactly = 1) { retentionCalculation.clearObsoleteContactDiaryLocationVisits() } - coVerify(exactly = 0) { retentionCalculation.clearObsoleteContactDiaryPersonEncounters() } + coVerify(exactly = 0) { + retentionCalculation.clearObsoleteContactDiaryPersonEncounters() + retentionCalculation.clearObsoleteRiskPerDate() + } result shouldNotBe null } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt index 12856945f0abb14dfe8d5b38ede8e18c2813b08b..c119d903c05470f741d87d364307b69498c3904e 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt @@ -3,6 +3,9 @@ package de.rki.coronawarnapp.contactdiary.retention import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.repo.DefaultContactDiaryRepository +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult +import de.rki.coronawarnapp.risk.storage.RiskLevelStorage +import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass import de.rki.coronawarnapp.util.TimeStamper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -16,6 +19,7 @@ import io.mockk.mockk import io.mockk.runs import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runBlockingTest +import org.joda.time.DateTime import org.joda.time.Instant import org.joda.time.LocalDate import org.junit.jupiter.api.AfterEach @@ -27,6 +31,7 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() { @MockK lateinit var timeStamper: TimeStamper @MockK lateinit var contactDiaryRepository: DefaultContactDiaryRepository + @MockK lateinit var riskLevelStorage: RiskLevelStorage private val testDates = arrayListOf<String>("2020-08-20T14:00:00.000Z", "2020-08-20T13:00:00.000Z", @@ -49,7 +54,8 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() { private fun createInstance() = ContactDiaryRetentionCalculation( timeStamper = timeStamper, - repository = contactDiaryRepository + repository = contactDiaryRepository, + riskLevelStorage = riskLevelStorage ) @Test @@ -66,6 +72,22 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() { instance.getDaysDiff(LocalDate(Instant.parse("2020-08-03T14:00:00.000Z"))) shouldBe 17 } + @Test + fun `filter by date`() { + val localDate = DateTime.parse("2020-08-20T23:00:00.000Z").toLocalDate() + + val instance = createInstance() + + instance.isOutOfRetention(localDate) shouldBe false + instance.isOutOfRetention(localDate.minusDays(5)) shouldBe false + instance.isOutOfRetention(localDate.minusDays(10)) shouldBe false + instance.isOutOfRetention(localDate.minusDays(15)) shouldBe false + instance.isOutOfRetention(localDate.minusDays(16)) shouldBe false + instance.isOutOfRetention(localDate.minusDays(17)) shouldBe true + instance.isOutOfRetention(localDate.minusDays(20)) shouldBe true + instance.isOutOfRetention(localDate.minusDays(25)) shouldBe true + } + @Test fun `test location visit deletion`() = runBlockingTest { val list: List<ContactDiaryLocationVisit> = testDates.map { createContactDiaryLocationVisit(Instant.parse(it)) } @@ -104,4 +126,25 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() { every { personEncounter.date } returns LocalDate(date) return personEncounter } + + @Test + fun `test risk per date results`() = runBlockingTest { + val instance = createInstance() + val list: List<AggregatedRiskPerDateResult> = testDates.map { createAggregatedRiskPerDateResult(Instant.parse(it)) } + val filteredList = list.filter { instance.isOutOfRetention(it.day) } + + every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(list) + coEvery { riskLevelStorage.deleteAggregatedRiskPerDateResults(any()) } just runs + + filteredList.size shouldBe 1 + instance.clearObsoleteRiskPerDate() + coVerify { riskLevelStorage.deleteAggregatedRiskPerDateResults(filteredList) } + } + + private fun createAggregatedRiskPerDateResult(date: Instant) = AggregatedRiskPerDateResult( + dateMillisSinceEpoch = date.millis, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + minimumDistinctEncountersWithLowRisk = 0, + minimumDistinctEncountersWithHighRisk = 0 + ) } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettingsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettingsTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f2827a44b88b15776010dc7cb49964edb6520d6d --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiarySettingsTest.kt @@ -0,0 +1,49 @@ +package de.rki.coronawarnapp.contactdiary.ui + +import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences +import de.rki.coronawarnapp.util.preferences.FlowPreference +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ContactDiarySettingsTest { + + @MockK lateinit var preferences: ContactDiaryPreferences + @MockK lateinit var intPreference: FlowPreference<Int> + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + every { preferences.onboardingStatusOrder } returns intPreference + } + + @Test + fun `not onboarded`() { + every { intPreference.value } returns 0 + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + } + + @Test + fun `bad preference values`() { + every { intPreference.value } returns 42 + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + + every { intPreference.value } returns -42 + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + + every { intPreference.value } returns Int.MAX_VALUE + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + + every { intPreference.value } returns Int.MIN_VALUE + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + } + + @Test + fun `onboarded`() { + every { intPreference.value } returns 1 + ContactDiarySettings(preferences).onboardingStatus shouldBe ContactDiarySettings.OnboardingStatus.RISK_STATUS_1_12 + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt index ea6e55c4697a6006634899eea2782364134d5f74..a9022f4fe16940addcb635c13383dec6c14083e1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt @@ -38,7 +38,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testOnDeleteAllLocationsClick() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.navigationEvent.observeForever { } viewModel.onDeleteAllLocationsClick() viewModel.navigationEvent.value shouldBe ContactDiaryEditLocationsViewModel.NavigationEvent.ShowDeletionConfirmationDialog @@ -49,7 +49,7 @@ class ContactDiaryEditLocationsViewModelTest { coEvery { contactDiaryRepository.deleteAllLocationVisits() } just Runs coEvery { contactDiaryRepository.deleteAllLocations() } just Runs every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.onDeleteAllConfirmedClick() coVerify(exactly = 1) { contactDiaryRepository.deleteAllLocationVisits() @@ -60,7 +60,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testOnEditLocationClick() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.navigationEvent.observeForever { } viewModel.onEditLocationClick(location) viewModel.navigationEvent.value shouldBe @@ -70,7 +70,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testIsButtonEnabled() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe true } @@ -78,7 +78,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testIsButtonNotEnabledWhenListIsEmpty() { every { contactDiaryRepository.locations } returns MutableStateFlow(emptyList()) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe false } @@ -86,7 +86,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testLocations() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.locationsLiveData.observeForever { } viewModel.locationsLiveData.value shouldBe locationList } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt index 183722bb6ea00c8c90bce2be935913aaa398a997..f1a8aa1e22d2f65e329e9dbfade18366738ab302 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt @@ -39,7 +39,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testOnDeleteAllLocationsClick() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.navigationEvent.observeForever { } viewModel.onDeleteAllPersonsClick() viewModel.navigationEvent.value shouldBe ContactDiaryEditPersonsViewModel.NavigationEvent.ShowDeletionConfirmationDialog @@ -50,7 +50,7 @@ class ContactDiaryEditPersonsViewModelTest { coEvery { contactDiaryRepository.deleteAllPeople() } just Runs coEvery { contactDiaryRepository.deleteAllPersonEncounters() } just Runs every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.onDeleteAllConfirmedClick() coVerify(exactly = 1) { contactDiaryRepository.deleteAllPeople() @@ -61,7 +61,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testOnEditLocationClick() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.navigationEvent.observeForever { } viewModel.onEditPersonClick(person) viewModel.navigationEvent.value shouldBe @@ -71,7 +71,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testIsButtonEnabled() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe true } @@ -79,7 +79,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testIsButtonNotEnabledWhenListIsEmpty() { every { contactDiaryRepository.people } returns MutableStateFlow(emptyList()) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe false } @@ -87,7 +87,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testLocations() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider) + viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) viewModel.personsLiveData.observeForever { } viewModel.personsLiveData.value shouldBe personList } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DayPackageSyncToolTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DayPackageSyncToolTest.kt index 598e8408c1e8e0aad0fd5f78c4940b13723fa664..17c4989677b24761b43f3e92b7d797310e5ac8f5 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DayPackageSyncToolTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DayPackageSyncToolTest.kt @@ -39,7 +39,7 @@ class DayPackageSyncToolTest : CommonSyncToolTest() { keyCache = keyCache, downloadTool = downloadTool, timeStamper = timeStamper, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), configProvider = configProvider ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncToolTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncToolTest.kt index 5e50eeaedd865915c26891ecd384e711160b6a7e..0a58b9578d7bf70ad5b1e1e0944f5c8525be7f12 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncToolTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/HourPackageSyncToolTest.kt @@ -41,7 +41,7 @@ class HourPackageSyncToolTest : CommonSyncToolTest() { downloadTool = downloadTool, timeStamper = timeStamper, configProvider = configProvider, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt index 871d43b357fb4d5a52e5d0ca30a6515a0c7026e4..10efd2034d8863d12170bfd6443ea0c5384580d1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt @@ -38,7 +38,7 @@ class MainActivityViewModelTest : BaseTest() { } private fun createInstance(): MainActivityViewModel = MainActivityViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), environmentSetup = environmentSetup, backgroundModeStatus = backgroundModeStatus ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt index 701dcece0bab0be0a2a494c6e45387bed0221fc2..f0361f63d6ede3955ee581c3358ac21d04908e3c 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt @@ -81,7 +81,7 @@ class HomeFragmentViewModelTest : BaseTest() { } private fun createInstance(): HomeFragmentViewModel = HomeFragmentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), errorResetTool = errorResetTool, tracingStatus = generalTracingStatus, tracingRepository = tracingRepository, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTrackerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTrackerTest.kt index 9012e23cf22f3a22d07d72703bbba373bf6788a9..823cee90f26d89f9abf72762878b116fead30ea1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTrackerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTrackerTest.kt @@ -56,7 +56,7 @@ class DefaultExposureDetectionTrackerTest : BaseTest() { private fun createInstance(scope: CoroutineScope) = DefaultExposureDetectionTracker( scope = scope, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), storage = storage, timeStamper = timeStamper, appConfigProvider = configProvider 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 index 4bd4076caffa27abfe8a250730495fbf917eeedc..8294b345482db09178e896662bf04d62084e450c 100644 --- 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 @@ -62,7 +62,7 @@ class ExposureStateUpdateReceiverTest : BaseTest() { val broadcastReceiverInjector = AndroidInjector<Any> { it as ExposureStateUpdateReceiver it.exposureDetectionTracker = exposureDetectionTracker - it.dispatcherProvider = TestDispatcherProvider + it.dispatcherProvider = TestDispatcherProvider() it.scope = scope it.workManager = workManager } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..2dfec80ff01f49ea9612996125645f4f25797b4e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt @@ -0,0 +1,43 @@ +package de.rki.coronawarnapp.risk.result + +import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass +import io.kotest.matchers.shouldBe +import org.joda.time.Days +import org.joda.time.Instant +import org.joda.time.LocalDate +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import timber.log.Timber + +class AggregatedRiskPerDateResultTest : BaseTest() { + + @Test + fun `day is correct`() { + val oneDay = Days.ONE.toStandardDuration() + val todayInstant = Instant.parse("2020-08-20T23:00:00.000Z") + val todayLocalDate = LocalDate.parse("2020-08-20") + val yesterdayInstant = todayInstant.minus(oneDay) + val yesterdayLocalDate = todayLocalDate.minusDays(1) + val tomorrowInstant = todayInstant.plus(oneDay) + val tomorrowLocalDate = todayLocalDate.plusDays(1) + + Timber.i("Today as LocalDate $todayLocalDate and as Instant $todayInstant") + Timber.i("Yesterday as LocalDate $yesterdayLocalDate and as Instant $yesterdayInstant") + Timber.i("Tomorrow as LocalDate $tomorrowLocalDate and as Instant $tomorrowInstant") + + val todayAggregatedRiskPerDateResult = createAggregatedRiskPerDateResult(todayInstant) + val yesterdayAggregatedRiskPerDateResult = createAggregatedRiskPerDateResult(yesterdayInstant) + val tomorrowAggregatedRiskPerDateResult = createAggregatedRiskPerDateResult(tomorrowInstant) + + todayAggregatedRiskPerDateResult.day shouldBe todayLocalDate + yesterdayAggregatedRiskPerDateResult.day shouldBe yesterdayLocalDate + tomorrowAggregatedRiskPerDateResult.day shouldBe tomorrowLocalDate + } + + private fun createAggregatedRiskPerDateResult(date: Instant) = AggregatedRiskPerDateResult( + dateMillisSinceEpoch = date.millis, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + minimumDistinctEncountersWithLowRisk = 0, + minimumDistinctEncountersWithHighRisk = 0 + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt index 9264e724ecc950c924ba5a4a385a25f4473ef231..1447b8c324728688fac6d66c90b68d09cac1bba5 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt @@ -1,17 +1,22 @@ package de.rki.coronawarnapp.risk.storage import de.rki.coronawarnapp.risk.RiskLevelResult +import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindow import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindowDaoWrapper +import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testPersistedAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testRiskLevelResultDao import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testRisklevelResult +import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testRisklevelResultWithAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase +import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase.AggregatedRiskPerDateResultDao import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase.ExposureWindowsDao import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase.Factory import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase.RiskResultsDao import de.rki.coronawarnapp.risk.storage.legacy.RiskLevelResultMigrator import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import io.mockk.Called import io.mockk.MockKAnnotations import io.mockk.Runs @@ -42,6 +47,7 @@ class BaseRiskLevelStorageTest : BaseTest() { @MockK lateinit var database: RiskResultDatabase @MockK lateinit var riskResultTables: RiskResultsDao @MockK lateinit var exposureWindowTables: ExposureWindowsDao + @MockK lateinit var aggregatedRiskPerDateResultDao: AggregatedRiskPerDateResultDao @MockK lateinit var riskLevelResultMigrator: RiskLevelResultMigrator @BeforeEach @@ -51,6 +57,7 @@ class BaseRiskLevelStorageTest : BaseTest() { every { databaseFactory.create() } returns database every { database.riskResults() } returns riskResultTables every { database.exposureWindows() } returns exposureWindowTables + every { database.aggregatedRiskPerDate() } returns aggregatedRiskPerDateResultDao every { database.clearAllTables() } just Runs coEvery { riskLevelResultMigrator.getLegacyResults() } returns emptyList() @@ -62,6 +69,9 @@ class BaseRiskLevelStorageTest : BaseTest() { coEvery { riskResultTables.deleteOldest(any()) } returns 7 every { exposureWindowTables.allEntries() } returns emptyFlow() + + every { aggregatedRiskPerDateResultDao.allEntries() } returns emptyFlow() + coEvery { aggregatedRiskPerDateResultDao.insertRisk(any()) } just Runs } @AfterEach @@ -90,6 +100,23 @@ class BaseRiskLevelStorageTest : BaseTest() { } } + @Test + fun `aggregatedRiskPerDateResults are returned from database and mapped`() { + val testPersistedAggregatedRiskPerDateResultFlow = flowOf(listOf(testPersistedAggregatedRiskPerDateResult)) + every { aggregatedRiskPerDateResultDao.allEntries() } returns testPersistedAggregatedRiskPerDateResultFlow + + runBlockingTest { + val instance = createInstance() + val allEntries = instance.aggregatedRiskPerDateResultTables.allEntries() + allEntries shouldBe testPersistedAggregatedRiskPerDateResultFlow + allEntries.first().map { it.toAggregatedRiskPerDateResult() } shouldBe listOf(testAggregatedRiskPerDateResult) + + val aggregatedRiskPerDateResults = instance.aggregatedRiskPerDateResults.first() + aggregatedRiskPerDateResults shouldNotBe listOf(testPersistedAggregatedRiskPerDateResult) + aggregatedRiskPerDateResults shouldBe listOf(testAggregatedRiskPerDateResult) + } + } + @Test fun `exposureWindows are returned from database and mapped`() { val testDaoWrappers = flowOf(listOf(testExposureWindowDaoWrapper)) @@ -247,6 +274,20 @@ class BaseRiskLevelStorageTest : BaseTest() { mockStoreWindows.invoke(any(), testRisklevelResult) mockDeleteOrphanedWindows.invoke() } + + coVerify(exactly = 0) { + aggregatedRiskPerDateResultDao.insertRisk(any()) + } + } + + @Test + fun `storing aggregatedRiskPerDateResults works`() = runBlockingTest { + val instance = createInstance() + instance.storeResult(testRisklevelResultWithAggregatedRiskPerDateResult) + + coVerify { + aggregatedRiskPerDateResultDao.insertRisk(any()) + } } @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt index 5c2582f83ebaaac2ccb9daff4d7776b02dad5227..3301437ffe8c94c9557de4a6e95643493a539cf7 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt @@ -3,7 +3,9 @@ package de.rki.coronawarnapp.risk.storage import com.google.android.gms.nearby.exposurenotification.ExposureWindow import com.google.android.gms.nearby.exposurenotification.ScanInstance import de.rki.coronawarnapp.risk.RiskLevelTaskResult +import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.result.AggregatedRiskResult +import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedAggregatedRiskPerDateResult import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDao import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDaoWrapper @@ -27,17 +29,19 @@ object RiskStorageTestData { ) ) + val testAggregatedRiskResult = AggregatedRiskResult( + totalRiskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + totalMinimumDistinctEncountersWithLowRisk = 1, + totalMinimumDistinctEncountersWithHighRisk = 2, + mostRecentDateWithLowRisk = Instant.ofEpochMilli(3), + mostRecentDateWithHighRisk = Instant.ofEpochMilli(4), + numberOfDaysWithLowRisk = 5, + numberOfDaysWithHighRisk = 6 + ) + val testRisklevelResult = RiskLevelTaskResult( calculatedAt = Instant.ofEpochMilli(9999L), - aggregatedRiskResult = AggregatedRiskResult( - totalRiskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, - totalMinimumDistinctEncountersWithLowRisk = 1, - totalMinimumDistinctEncountersWithHighRisk = 2, - mostRecentDateWithLowRisk = Instant.ofEpochMilli(3), - mostRecentDateWithHighRisk = Instant.ofEpochMilli(4), - numberOfDaysWithLowRisk = 5, - numberOfDaysWithHighRisk = 6 - ), + aggregatedRiskResult = testAggregatedRiskResult, exposureWindows = null ) @@ -70,4 +74,24 @@ object RiskStorageTestData { setTypicalAttenuationDb(30) }.build().let { setScanInstances(listOf(it)) } }.build() + + val testAggregatedRiskPerDateResult = AggregatedRiskPerDateResult( + dateMillisSinceEpoch = 9999L, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + minimumDistinctEncountersWithLowRisk = 0, + minimumDistinctEncountersWithHighRisk = 0 + ) + + val testPersistedAggregatedRiskPerDateResult = PersistedAggregatedRiskPerDateResult( + dateMillisSinceEpoch = 9999L, + riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH, + minimumDistinctEncountersWithLowRisk = 0, + minimumDistinctEncountersWithHighRisk = 0 + ) + + val testRisklevelResultWithAggregatedRiskPerDateResult = testRisklevelResult.copy( + aggregatedRiskResult = testAggregatedRiskResult.copy( + aggregatedRiskPerDateResults = listOf(testAggregatedRiskPerDateResult) + ) + ) } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsDataTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsDataTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..065349ad9360b19352d57cf49a05ca4af5016cbb --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsDataTest.kt @@ -0,0 +1,128 @@ +package de.rki.coronawarnapp.statistics + +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.joda.time.Instant +import org.junit.jupiter.api.Test + +class StatisticsDataTest { + + @Test + fun `infection mapping`() { + val stats = InfectionStats( + updatedAt = Instant.EPOCH, + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.0 + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 2.0 + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 3.0 + }.build() + ) + ) + stats.apply { + cardType shouldBe StatsItem.Type.INFECTION + cardType.id shouldBe 1 + newInfections.value shouldBe 1.0 + sevenDayAverage.value shouldBe 2.0 + total.value shouldBe 3.0 + } + + stats.requireValidity() + + shouldThrow<IllegalArgumentException> { + stats.copy(keyFigures = stats.keyFigures.subList(0, 1)).requireValidity() + } + } + + @Test + fun `incidence mapping`() { + val stats = IncidenceStats( + updatedAt = Instant.EPOCH, + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.0 + }.build() + ) + ) + stats.apply { + cardType shouldBe StatsItem.Type.INCIDENCE + cardType.id shouldBe 2 + sevenDayIncidence.value shouldBe 1.0 + } + + stats.requireValidity() + + shouldThrow<IllegalArgumentException> { + stats.copy(keyFigures = emptyList()).requireValidity() + } + } + + @Test + fun `keysubmission mapping`() { + val stats = KeySubmissionsStats( + updatedAt = Instant.EPOCH, + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.0 + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 2.0 + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 3.0 + }.build() + ) + ) + + stats.apply { + cardType shouldBe StatsItem.Type.KEYSUBMISSION + cardType.id shouldBe 3 + keySubmissions.value shouldBe 1.0 + sevenDayAverage.value shouldBe 2.0 + total.value shouldBe 3.0 + } + + stats.requireValidity() + + shouldThrow<IllegalArgumentException> { + stats.copy(keyFigures = stats.keyFigures.subList(0, 1)).requireValidity() + } + } + + @Test + fun `7 day R value mapping`() { + val stats = SevenDayRValue( + updatedAt = Instant.EPOCH, + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.0 + }.build() + ) + ) + + stats.apply { + cardType shouldBe StatsItem.Type.SEVEN_DAY_RVALUE + cardType.id shouldBe 4 + reproductionNumber.value shouldBe 1.0 + } + + stats.requireValidity() + + shouldThrow<IllegalArgumentException> { + stats.copy(keyFigures = emptyList()).requireValidity() + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsModuleTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsModuleTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..393a62e60ed3da9cff0af8ef0490c3e082124a6f --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/StatisticsModuleTest.kt @@ -0,0 +1,45 @@ +package de.rki.coronawarnapp.statistics + +import android.content.Context +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseIOTest +import java.io.File + +class StatisticsModuleTest : BaseIOTest() { + @MockK lateinit var context: Context + + private val testDir = File(IO_TEST_BASEDIR, this::class.java.simpleName) + private val cacheFiles = File(testDir, "cache") + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + every { context.cacheDir } returns cacheFiles + + testDir.mkdirs() + testDir.exists() shouldBe true + } + + @AfterEach + fun teardown() { + clearAllMocks() + testDir.deleteRecursively() + } + + private fun createModule() = StatisticsModule() + + @Test + fun `sideeffect free instantiation`() { + shouldNotThrowAny { + createModule() + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsAPIV1Test.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsAPIV1Test.kt new file mode 100644 index 0000000000000000000000000000000000000000..62a262fa455d77f730648819dfd6996e4e81847d --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsAPIV1Test.kt @@ -0,0 +1,81 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.environment.download.DownloadCDNModule +import de.rki.coronawarnapp.http.HttpModule +import de.rki.coronawarnapp.statistics.StatisticsModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import kotlinx.coroutines.runBlocking +import okhttp3.ConnectionSpec +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseIOTest +import java.io.File +import java.util.concurrent.TimeUnit + +class StatisticsAPIV1Test : BaseIOTest() { + + private lateinit var webServer: MockWebServer + private lateinit var serverAddress: String + + private val testDir = File(IO_TEST_BASEDIR, this::class.java.simpleName) + private val cacheDir = File(testDir, "cacheDir") + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + webServer = MockWebServer() + webServer.start() + serverAddress = "http://${webServer.hostName}:${webServer.port}" + } + + @AfterEach + fun teardown() { + clearAllMocks() + webServer.shutdown() + testDir.deleteRecursively() + } + + private fun createAPI(): StatisticsApiV1 { + val httpModule = HttpModule() + val defaultHttpClient = httpModule.defaultHttpClient() + val gsonConverterFactory = httpModule.provideGSONConverter() + + val cdnHttpClient = DownloadCDNModule() + .cdnHttpClient(defaultHttpClient) + .newBuilder() + .connectionSpecs(listOf(ConnectionSpec.CLEARTEXT, ConnectionSpec.MODERN_TLS)) + .build() + + val cache = StatisticsModule().httpCache(cacheDir) + + return StatisticsModule().api( + client = cdnHttpClient, + url = serverAddress, + gsonConverterFactory = gsonConverterFactory, + cache = cache + ) + } + + @Test + fun `application config download`() { + val api = createAPI() + + webServer.enqueue(MockResponse().setBody("~look at me, I'm statistics")) + + runBlocking { + api.getStatistics().apply { + body()!!.string() shouldBe "~look at me, I'm statistics" + } + } + + val request = webServer.takeRequest(5, TimeUnit.SECONDS)!! + request.method shouldBe "GET" + request.path shouldBe "/version/v1/stats" + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsCacheTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsCacheTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..cfdca2dcb3102041d1fa05a7d024dc4fb1790646 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsCacheTest.kt @@ -0,0 +1,62 @@ +package de.rki.coronawarnapp.statistics.source + +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseIOTest +import java.io.File + +class StatisticsCacheTest : BaseIOTest() { + + private val testDir = File(IO_TEST_BASEDIR, this::class.java.simpleName) + private val androidCacheDir = File(testDir, "cache") + private val statisticsCacheDir = File(androidCacheDir, "statistics") + + private val cacheFile = File(statisticsCacheDir, "cache_raw") + + private val testData = "Row, Row, Row Your Boat".encodeToByteArray() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + } + + @AfterEach + fun teardown() { + testDir.deleteRecursively() + } + + fun createInstance() = StatisticsCache( + cacheDir = statisticsCacheDir + ) + + @Test + fun `empty start`() { + createInstance().load() shouldBe null + cacheFile.exists() shouldBe false + } + + @Test + fun `loading data`() { + cacheFile.parentFile?.mkdirs() + cacheFile.writeBytes(testData) + createInstance().load() shouldBe testData + } + + @Test + fun `saving data`() { + cacheFile.exists() shouldBe false + createInstance().save(testData) + cacheFile.readBytes() shouldBe testData + } + + @Test + fun `deleting data`() { + cacheFile.parentFile?.mkdirs() + cacheFile.writeBytes(testData) + createInstance().save(null) + cacheFile.exists() shouldBe false + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsParserTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..86973e79c858b2369a48b34e8add2844a89e027b --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsParserTest.kt @@ -0,0 +1,324 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.server.protocols.internal.stats.CardHeaderOuterClass +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.server.protocols.internal.stats.StatisticsOuterClass +import de.rki.coronawarnapp.statistics.IncidenceStats +import de.rki.coronawarnapp.statistics.InfectionStats +import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.SevenDayRValue +import de.rki.coronawarnapp.statistics.StatisticsData +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import org.joda.time.Instant +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class StatisticsParserTest : BaseTest() { + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + } + + @AfterEach + fun teardown() { + clearAllMocks() + } + + private fun createInstance() = StatisticsParser() + + @Test + fun `default parsing of all types`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addAllCardIdSequence(listOf(1, 3, 2, 4)) + addKeyFigureCards(INFECTION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + addKeyFigureCards(INCIDENCE_PROTO) + addKeyFigureCards(SEVENDAYRVALUE_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(INFECTION_STATS, KEYSUBMISSION_STATS, INCIDENCE_STATS, SEVENDAYRVALUE_STATS) + ) + } + + @Test + fun `handle empty statistics data`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData() + } + + @Test + fun `handle hidden card for which we have data`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + addKeyFigureCards(INFECTION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS) + ) + } + + @Test + fun `handle corrupt card data`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + addCardIdSequence(1) + INFECTION_PROTO.toBuilder().apply { + removeKeyFigures(2) + }.build().let { addKeyFigureCards(it) } + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS) + ) + } + + @Test + fun `handle duplicate card data`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + addCardIdSequence(1) + addCardIdSequence(3) + addKeyFigureCards(INFECTION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS, INFECTION_STATS, KEYSUBMISSION_STATS) + ) + } + + @Test + fun `handle duplicate id in card sequence without crash`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + addCardIdSequence(1) + addCardIdSequence(3) + addKeyFigureCards(INFECTION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS, INFECTION_STATS, KEYSUBMISSION_STATS) + ) + } + + @Test + fun `handle unknown keycard data`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + + INFECTION_PROTO.newBuilderForType().apply { + header = this.header.toBuilder().apply { + cardId = 99 + }.build() + }.build().let { addKeyFigureCards(it) } + + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS) + ) + } + + @Test + fun `handle unknown id in card sequence`() { + val statisticsProto = StatisticsOuterClass.Statistics.newBuilder().apply { + addCardIdSequence(3) + addCardIdSequence(99) + addKeyFigureCards(INFECTION_PROTO) + addKeyFigureCards(KEYSUBMISSION_PROTO) + }.build().toByteArray() + createInstance().parse(statisticsProto) shouldBe StatisticsData( + listOf(KEYSUBMISSION_STATS) + ) + } + + companion object { + val INFECTION_PROTO = KeyFigureCardOuterClass.KeyFigureCard.newBuilder().apply { + CardHeaderOuterClass.CardHeader.newBuilder().apply { + cardId = 1 + updatedAt = 123456778890 + }.build().let { header = it } + listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 14714.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 11981.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING + trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build(), KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 429181.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ).let { addAllKeyFigures(it) } + }.build() + + val INFECTION_STATS = InfectionStats( + updatedAt = Instant.ofEpochSecond(123456778890), + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 14714.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 11981.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING + trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build(), KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 429181.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ) + ) + + val INCIDENCE_PROTO = KeyFigureCardOuterClass.KeyFigureCard.newBuilder().apply { + CardHeaderOuterClass.CardHeader.newBuilder().apply { + cardId = 2 + updatedAt = 1604839761 + }.build().let { header = it } + listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 98.9 + decimals = 1 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ).let { addAllKeyFigures(it) } + }.build() + + val INCIDENCE_STATS = IncidenceStats( + updatedAt = Instant.ofEpochSecond(1604839761), + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 98.9 + decimals = 1 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ) + ) + + val KEYSUBMISSION_PROTO = KeyFigureCardOuterClass.KeyFigureCard.newBuilder().apply { + CardHeaderOuterClass.CardHeader.newBuilder().apply { + cardId = 3 + updatedAt = 0 + }.build().let { header = it } + listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1514.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 1812.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.DECREASING + trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 20922.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ).let { addAllKeyFigures(it) } + }.build() + + val KEYSUBMISSION_STATS = KeySubmissionsStats( + updatedAt = Instant.ofEpochSecond(0), + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1514.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.SECONDARY + value = 1812.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.DECREASING + trendSemantic = KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build(), + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.TERTIARY + value = 20922.0 + decimals = 0 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.UNSPECIFIED_TREND + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + }.build() + ) + ) + + val SEVENDAYRVALUE_PROTO = KeyFigureCardOuterClass.KeyFigureCard.newBuilder().apply { + CardHeaderOuterClass.CardHeader.newBuilder().apply { + cardId = 4 + updatedAt = 1604839761 + }.build().let { header = it } + listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.04 + decimals = 2 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build() + ).let { addAllKeyFigures(it) } + }.build() + + val SEVENDAYRVALUE_STATS = SevenDayRValue( + updatedAt = Instant.ofEpochSecond(1604839761), + keyFigures = listOf( + KeyFigureCardOuterClass.KeyFigure.newBuilder().apply { + rank = KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY + value = 1.04 + decimals = 2 + trend = KeyFigureCardOuterClass.KeyFigure.Trend.INCREASING + trendSemantic = + KeyFigureCardOuterClass.KeyFigure.TrendSemantic.NEGATIVE + }.build() + ) + ) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsProviderTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..78498114ab1ca2a2a273ffd4f0cd20e97b0a4f46 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsProviderTest.kt @@ -0,0 +1,164 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.statistics.StatisticsData +import de.rki.coronawarnapp.util.device.ForegroundState +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.coVerifySequence +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.asDispatcherProvider +import testhelpers.coroutines.runBlockingTest2 +import testhelpers.coroutines.test +import java.io.IOException + +class StatisticsProviderTest : BaseTest() { + @MockK lateinit var server: StatisticsServer + @MockK lateinit var localCache: StatisticsCache + @MockK lateinit var parser: StatisticsParser + @MockK lateinit var foregroundState: ForegroundState + @MockK lateinit var statisticsData: StatisticsData + + private val testData = "ABC".encodeToByteArray() + + private val testForegroundState = MutableStateFlow(false) + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + server.apply { + coEvery { getRawStatistics() } returns testData + every { clear() } just Runs + } + + every { parser.parse(testData) } returns statisticsData + every { foregroundState.isInForeground } returns testForegroundState + + localCache.apply { + var testLocalCache: ByteArray? = null + every { load() } answers { testLocalCache } + every { save(any()) } answers { testLocalCache = arg(0) } + } + } + + @AfterEach + fun teardown() { + clearAllMocks() + } + + fun createInstance(scope: CoroutineScope) = StatisticsProvider( + server = server, + scope = scope, + localCache = localCache, + parser = parser, + foregroundState = foregroundState, + dispatcherProvider = scope.asDispatcherProvider() + ) + + @Test + fun `creation is sideeffect free`() = runBlockingTest2(ignoreActive = true) { + createInstance(this) + verify(exactly = 0) { localCache.load() } + } + + @Test + fun `initial subscription tries cache, then server`() = runBlockingTest2(ignoreActive = true) { + val testCollector = createInstance(this).current.test(startOnScope = this) + + coVerifySequence { + localCache.load() + server.getRawStatistics() + parser.parse(testData) + localCache.save(testData) + } + + testCollector.latestValue shouldBe statisticsData + } + + @Test + fun `update foreground state change`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance(this) + val testCollector = instance.current.test(startOnScope = this) + + testCollector.latestValue shouldBe statisticsData + + val newRawStatisticsData = "Bernd".encodeToByteArray() + coEvery { server.getRawStatistics() } returns newRawStatisticsData + val newStatisticsData = mockk<StatisticsData>() + coEvery { parser.parse(any()) } returns newStatisticsData + + testForegroundState.value = false + testForegroundState.value = true + + testCollector.latestValues shouldBe listOf(statisticsData, newStatisticsData) + verify { localCache.save(newRawStatisticsData) } + } + + @Test + fun `failed update does not destroy cache`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance(this) + val testCollector = instance.current.test(startOnScope = this) + + testCollector.latestValue shouldBe statisticsData + + coEvery { server.getRawStatistics() } throws IOException() + + instance.triggerUpdate() + + testCollector.latestValues shouldBe listOf(statisticsData) + verify(exactly = 0) { localCache.save(null) } + } + + @Test + fun `clear deletes cache`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance(this) + + val testCollector = instance.current.test(startOnScope = this) + testCollector.latestValue shouldBe statisticsData + + instance.clear() + + coVerify { + server.clear() + localCache.save(null) + } + } + + @Test + fun `subscription flow timeout is 5 seconds`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance(this) + var testCollector1 = instance.current.test(startOnScope = this) + var testCollector2 = instance.current.test(startOnScope = this) + + advanceUntilIdle() + coVerify(exactly = 1) { localCache.load() } + + testCollector1.cancel() + testCollector2.cancel() + + advanceTimeBy(6000) + + testCollector1 = instance.current.test(startOnScope = this) + testCollector2 = instance.current.test(startOnScope = this) + + advanceUntilIdle() + coVerify(exactly = 2) { localCache.load() } + + testCollector1.cancel() + testCollector2.cancel() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsServerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsServerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..5a13d25527a2431b34a94c658e5f3057481cb2e4 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/source/StatisticsServerTest.kt @@ -0,0 +1,114 @@ +package de.rki.coronawarnapp.statistics.source + +import de.rki.coronawarnapp.util.security.VerificationKeys +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.verify +import kotlinx.coroutines.test.runBlockingTest +import okhttp3.Cache +import okhttp3.ResponseBody.Companion.toResponseBody +import okio.ByteString.Companion.decodeHex +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import retrofit2.Response +import testhelpers.BaseIOTest +import java.io.IOException + +class StatisticsServerTest : BaseIOTest() { + + @MockK lateinit var api: StatisticsApiV1 + @MockK lateinit var verificationKeys: VerificationKeys + @MockK lateinit var cache: Cache + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { verificationKeys.hasInvalidSignature(any(), any()) } returns false + every { cache.evictAll() } just Runs + } + + @AfterEach + fun teardown() { + clearAllMocks() + } + + private fun createInstance() = StatisticsServer( + api = { api }, + verificationKeys = verificationKeys, + cache = cache + ) + + @Test + fun `successful download`() = runBlockingTest { + coEvery { api.getStatistics() } returns Response.success(STATS_ZIP.toResponseBody()) + + val server = createInstance() + + val rawStatistics = server.getRawStatistics() + rawStatistics shouldBe STATS_PROTO + + verify(exactly = 1) { verificationKeys.hasInvalidSignature(any(), any()) } + } + + @Test + fun `data is faulty`() = runBlockingTest { + coEvery { api.getStatistics() } returns Response.success("123ABC".decodeHex().toResponseBody()) + + val server = createInstance() + + shouldThrow<IOException> { + server.getRawStatistics() + } + } + + @Test + fun `verification fails`() = runBlockingTest { + coEvery { api.getStatistics() } returns Response.success(STATS_ZIP.toResponseBody()) + every { verificationKeys.hasInvalidSignature(any(), any()) } returns true + + val server = createInstance() + + shouldThrow<InvalidStatisticsSignatureException> { + server.getRawStatistics() + } + } + + @Test + fun `clear clears cache`() { + createInstance().clear() + verify { cache.evictAll() } + } + + companion object { + private val STATS_ZIP = + ( + "504b03041400080808008d4a2f520000000000000000000000000a0000006578706f72742e736967018b0074ff0a88010a380a" + + "1864652e726b692e636f726f6e617761726e6170702d6465761a02763122033236322a13312e322e3834302e3130303435" + + "2e342e332e321001180122483046022100f363cc4813367bdd2fa03c91fc49c3521abf2db86ec8f5836f97f5d13f915285" + + "022100e8a51c68ece56ccc44de41cee75d766adb5f1d688d1773875dc1c6944bc2a1de504b0708a4db7bfc900000008b00" + + "0000504b03041400080808008d4a2f520000000000000000000000000a0000006578706f72742e62696ee3626164666211" + + "32e5e2e060146898fcef3fab103707a3200308dcb8ea20c4cfc104e6386cb9e4a0c0a4c10c9465060b1c5a69e728240bd4" + + "c604d52608d41621e1f3e19e73928304a302b30613d8546674535f34204c6560a8725060d460849bfac18d11622a8b40c3" + + "93c750538376c8b5be0efc602fc104520b00504b070867e7aa477b000000b2000000504b010214001400080808008d4a2f" + + "52a4db7bfc900000008b0000000a00000000000000000000000000000000006578706f72742e736967504b010214001400" + + "080808008d4a2f5267e7aa477b000000b20000000a00000000000000000000000000c80000006578706f72742e62696e50" + + "4b05060000000002000200700000007b0100000000" + ).decodeHex() + private val STATS_PROTO = + ( + "0a040103020412350a080801108093feff05120b0801110000000000d8d540120f0802110000000040b4d24020022803120b08" + + "031100000000c2a93e41121d0a080802108093feff05121108011158184cf0de43624018012003280212350a0808031080" + + "93feff05120b0801110000000000e88040120f0802110000000000007a4020012801120b08031100000000f0460141121d" + + "0a0808041080e4e3ff05121108011152b81e85eb51f03f180220012801" + ).decodeHex().toByteArray() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCardItemTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCardItemTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..07ba38aec8b7ace5033995d3fcfb90cf76c203d6 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/ui/homecards/StatisticsHomeCardItemTest.kt @@ -0,0 +1,35 @@ +package de.rki.coronawarnapp.statistics.ui.homecards + +import de.rki.coronawarnapp.statistics.StatisticsData +import io.kotest.assertions.fail +import io.kotest.matchers.shouldBe +import org.junit.Test + +internal class StatisticsHomeCardItemTest { + + @Test + fun `test equals() of statistics item should return true when onHelpAction click listener is different`() { + + val itemWithClickListener1 = StatisticsHomeCard.Item(StatisticsData(emptyList())) { + // ClickListener1 + } + + val itemWithClickListener2 = StatisticsHomeCard.Item(StatisticsData(emptyList())) { + // ClickListener2 + } + + // Check if click listeners are actually different + if (itemWithClickListener1.onHelpAction == itemWithClickListener2.onHelpAction) { + fail("Different Click Listeners should be set to StatisticsHomeCard.Item") + } + + // basic assertion + (itemWithClickListener1 == itemWithClickListener2) shouldBe true + + // assert symmetry + (itemWithClickListener2 == itemWithClickListener1) shouldBe true + + // assert reflexivity + (itemWithClickListener1 == itemWithClickListener1) shouldBe true + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatterTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatterTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..7065b23afe2f7cf33d7da19bc623aa65d70c8821 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/statistics/util/StatisticsNumberValueFormatterTest.kt @@ -0,0 +1,79 @@ +package de.rki.coronawarnapp.statistics.util + +import android.content.Context +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.contactdiary.util.getLocale +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.Locale + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +internal class StatisticsNumberValueFormatterTest { + + @MockK + private lateinit var context: Context + + @BeforeAll + fun setUp() { + MockKAnnotations.init(this) + mockkStatic("de.rki.coronawarnapp.contactdiary.util.ContactDiaryExtensionsKt") + every { context.getString(R.string.statistics_value_suffix_million) } returns "Mio." + } + + @ParameterizedTest + @MethodSource("provideArguments") + fun `getFormattedNumberValue() should return correctly formatted number`( + value: Double, + decimals: Int, + locale: Locale, + expected: String + ) { + + every { context.getLocale() } returns locale + + formatStatisticalValue(context, value, decimals) shouldBe expected + } + + companion object { + + @Suppress("unused") + @JvmStatic + fun provideArguments() = listOf( + + Arguments.of(12.3456, -1, Locale.GERMANY, "12"), + Arguments.of(12.3456, 0, Locale.GERMANY, "12"), + Arguments.of(12.3456, 1, Locale.GERMANY, "12,3"), + Arguments.of(12.3456, 2, Locale.GERMANY, "12,35"), + + Arguments.of(12.3456, 1, Locale.UK, "12.3"), + + Arguments.of(12.6543, -1, Locale.GERMANY, "13"), + Arguments.of(12.6543, 0, Locale.GERMANY, "13"), + Arguments.of(12.6543, 1, Locale.GERMANY, "12,7"), + Arguments.of(12.6543, 2, Locale.GERMANY, "12,65"), + + Arguments.of(9_999_999, 0, Locale.GERMANY, "9.999.999"), + Arguments.of(9_999_999, 0, Locale.UK, "9,999,999"), + + Arguments.of(10_000_000, 0, Locale.GERMANY, "10,0 Mio."), + Arguments.of(12_345_678, 0, Locale.GERMANY, "12,3 Mio."), + Arguments.of(12_654_321, 0, Locale.GERMANY, "12,7 Mio."), + Arguments.of(12_654_321, 0, Locale.UK, "12.7 Mio.") + ) + } + + @AfterAll + fun cleanUp() { + unmockkAll() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt index 87c6c0f008a10f8fc3c5ff0a2b4cb8b4f5c876ec..45a8b3e1ef2b03347324d72f0a8c4ad9ca32b37d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt @@ -29,7 +29,7 @@ class InteroperabilityConfigurationFragmentViewModelTest { } private fun createViewModel() = - InteroperabilityConfigurationFragmentViewModel(interoperabilityRepository, TestDispatcherProvider) + InteroperabilityConfigurationFragmentViewModel(interoperabilityRepository, TestDispatcherProvider()) @Test fun `viewmodel returns interop repo countryList`() { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt index 343f7b9609c1ee4baf50c2b74dbf63bf1ac2b47d..63b1203140a7d028f8dce3f052407a3439210f97 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt @@ -34,7 +34,7 @@ class LauncherActivityViewModelTest { private fun createViewModel() = LauncherActivityViewModel( updateChecker = updateChecker, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/notification/NotificationSettingsFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/notification/NotificationSettingsFragmentViewModelTest.kt index a1bcbcdf494e6cc0575aa48e6c9d4114057e79e2..031ff0e1d3dcbfb2836a1be87e754d4d3061380a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/notification/NotificationSettingsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/notification/NotificationSettingsFragmentViewModelTest.kt @@ -43,7 +43,7 @@ class NotificationSettingsFragmentViewModelTest : BaseTest() { private fun createInstance(): NotificationSettingsFragmentViewModel = NotificationSettingsFragmentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), notificationSettings = notificationSettings ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModelTest.kt index 115d261ecf50446fd5532d62f8de0ebb4fa8d8fd..7b48a9b4900cbc282bbd0645a02599123d388b5d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/settings/start/SettingsFragmentViewModelTest.kt @@ -46,7 +46,7 @@ class SettingsFragmentViewModelTest : BaseTest() { } private fun createInstance(): SettingsFragmentViewModel = SettingsFragmentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), tracingStatus = tracingStatus, backgroundModeStatus = backgroundModeStatus, notificationSettings = notificationSettings diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt index 151651436838878fcc4cd85133c3c06ae578dabf..585c1c37fdc896b919972326fc41ed1bab736def 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt @@ -36,7 +36,7 @@ class SubmissionConsentViewModelTest { viewModel = SubmissionConsentViewModel( submissionRepository, interoperabilityRepository, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt index 591b391e48ba7638d225c1bbfc462086d6a51476..1a07365dca30a267deacd0b950e0bcdd59b8cd85 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt @@ -53,7 +53,7 @@ class SubmissionSymptomCalendarViewModelTest : BaseTest() { private fun createViewModel(indication: Symptoms.Indication = Symptoms.Indication.POSITIVE) = SubmissionSymptomCalendarViewModel( symptomIndication = indication, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), submissionRepository = submissionRepository, autoSubmission = autoSubmission ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt index f3e1ee087cf6863a7b071919360b948ce16b9b50..af78444e2651187a3b35b01c2454269393db8618 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt @@ -47,7 +47,7 @@ class SubmissionSymptomIntroductionViewModelTest : BaseTest() { } private fun createViewModel() = SubmissionSymptomIntroductionViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), submissionRepository = submissionRepository, autoSubmission = autoSubmission ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt index 8d0f77ea5c10ac01d76fcd2b611ac8608c05e274..22e7c02b1eef4f0900dc86a165b48a7ddfa91250 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt @@ -18,7 +18,7 @@ class SubmissionTanViewModelTest : BaseTest() { @MockK lateinit var submissionRepository: SubmissionRepository private fun createInstance() = SubmissionTanViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), submissionRepository = submissionRepository ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt index 1341d2771a2d3468a9d7853394fb84827de7a550..9be0ec3c971e726d87ea1543eec2490805a56dd1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testavailable/SubmissionTestResultAvailableViewModelTest.kt @@ -46,7 +46,7 @@ class SubmissionTestResultAvailableViewModelTest : BaseTest() { private fun createViewModel(): SubmissionTestResultAvailableViewModel = SubmissionTestResultAvailableViewModel( submissionRepository = submissionRepository, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), tekHistoryUpdaterFactory = tekHistoryUpdaterFactory, autoSubmission = autoSubmission ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt index 9cf860c68b74bc78a2b50c7d5b7f9f98d1a7df76..c84ea408def3e5f45cba933fdcde2aaf483b497c 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt @@ -29,7 +29,7 @@ class SubmissionTestResultConsentGivenViewModelTest : BaseTest() { private fun createViewModel() = SubmissionTestResultConsentGivenViewModel( submissionRepository = submissionRepository, - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), autoSubmission = autoSubmission, testResultAvailableNotificationService = testResultAvailableNotificationService ) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt index b9a3cd2ba311145208f19c1af628ee876f012c21..d87ada61d16654e5a2e13d3dba3d9bab0034644b 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningNoConsentViewModelTest.kt @@ -47,7 +47,7 @@ class SubmissionResultPositiveOtherWarningNoConsentViewModelTest : BaseTest() { } private fun createViewModel() = SubmissionResultPositiveOtherWarningNoConsentViewModel( - dispatcherProvider = TestDispatcherProvider, + dispatcherProvider = TestDispatcherProvider(), tekHistoryUpdaterFactory = tekHistoryUpdaterFactory, autoSubmission = autoSubmission, enfClient = enfClient, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt index 184fae9da67cfb236f7f7f650b6db6df663736e0..d5a70f44c58d3020649dfa8ee117caa5833bc1a6 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/yourconsent/SubmissionYourConsentViewModelTest.kt @@ -43,7 +43,7 @@ class SubmissionYourConsentViewModelTest : BaseTest() { private fun createViewModel(): SubmissionYourConsentViewModel = SubmissionYourConsentViewModel( interoperabilityRepository = interoperabilityRepository, submissionRepository = submissionRepository, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @AfterEach diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt index 57ef31fc9325943dba13c67b8875303be582671a..cbb714147ba97de16341eca6400c3a0363b63ea6 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/device/ForegroundStateTest.kt @@ -12,9 +12,12 @@ import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith import testhelpers.BaseTest import testhelpers.coroutines.test +import testhelpers.extensions.InstantExecutorExtension +@ExtendWith(InstantExecutorExtension::class) class ForegroundStateTest : BaseTest() { @MockK lateinit var lifecycleOwner: LifecycleOwner diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterStatisticsHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterStatisticsHelperTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..671a8abcec048f9bc2f4238fbf92ebde72505ae0 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterStatisticsHelperTest.kt @@ -0,0 +1,105 @@ +package de.rki.coronawarnapp.util.formatter + +import android.content.Context +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.contactdiary.util.getLocale +import de.rki.coronawarnapp.statistics.IncidenceStats +import de.rki.coronawarnapp.statistics.InfectionStats +import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.SevenDayRValue +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkAll +import org.joda.time.DateTime +import org.joda.time.Instant +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.util.Locale + +class FormatterStatisticsHelperTest { + + @MockK + private lateinit var context: Context + + private val today = Instant() + private val yesterday = DateTime().minusDays(1).toInstant() + + @Before + fun setUp() { + val slot = slot<String>() + MockKAnnotations.init(this) + mockkStatic("de.rki.coronawarnapp.contactdiary.util.ContactDiaryExtensionsKt") + with(context) { + every { context.getLocale() } returns Locale.GERMANY + every { getString(R.string.statistics_primary_value_current) } returns CURRENT + every { getString(R.string.statistics_primary_value_today) } returns TODAY + every { getString(R.string.statistics_primary_value_yesterday) } returns YESTERDAY + every { getString(R.string.statistics_primary_value_until_today) } returns UNTIL_TODAY + every { getString(R.string.statistics_primary_value_until_yesterday) } returns UNTIL_YESTERDAY + every { + getString( + R.string.statistics_primary_value_until, + capture(slot) + ) + } answers { "Until ${slot.captured}" } + } + } + + @Test + fun `InfectionStats primary label test`() { + InfectionStats(today, listOf()).getPrimaryLabel(context) shouldBe TODAY + InfectionStats(yesterday, listOf()).getPrimaryLabel(context) shouldBe YESTERDAY + InfectionStats( + Instant.parse("2021-01-13T00:00:00Z"), + listOf() + ).getPrimaryLabel(context) shouldBe "13.01.2021" + } + + @Test + fun `KeySubmissionsStats primary label test`() { + KeySubmissionsStats(today, listOf()).getPrimaryLabel(context) shouldBe TODAY + KeySubmissionsStats(yesterday, listOf()).getPrimaryLabel(context) shouldBe YESTERDAY + KeySubmissionsStats( + Instant.parse("2021-01-13T00:00:00Z"), + listOf() + ).getPrimaryLabel(context) shouldBe "13.01.2021" + } + + @Test + fun `IncidenceStats primary label test`() { + IncidenceStats(today, listOf()).getPrimaryLabel(context) shouldBe UNTIL_TODAY + IncidenceStats(yesterday, listOf()).getPrimaryLabel(context) shouldBe UNTIL_YESTERDAY + IncidenceStats( + Instant.parse("2021-01-13T00:00:00Z"), + listOf() + ).getPrimaryLabel(context) shouldBe "Until 13.01.2021" + } + + @Test + fun `SevenDayRValue primary label test`() { + SevenDayRValue(today, listOf()).getPrimaryLabel(context) shouldBe CURRENT + SevenDayRValue(yesterday, listOf()).getPrimaryLabel(context) shouldBe YESTERDAY + SevenDayRValue( + Instant.parse("2021-01-13T00:00:00Z"), + listOf() + ).getPrimaryLabel(context) shouldBe "Until 13.01.2021" + } + + @After + fun cleanUp() { + unmockkAll() + } + + companion object { + private const val TODAY = "Today" + private const val CURRENT = "Current" + private const val YESTERDAY = "Yesterday" + private const val UNTIL_TODAY = "Until today" + private const val UNTIL_YESTERDAY = "Until yesterday" + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt index 7d8d75d1183497bb55efc76b55d4742d72046b8b..725775cbee07d9d3d010358c3e8ade3c806a0457 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt @@ -15,7 +15,6 @@ import de.rki.coronawarnapp.playbook.Playbook import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.util.di.AppContext -import de.rki.coronawarnapp.util.di.AssistedInjectModule import de.rki.coronawarnapp.util.serialization.BaseGson import io.github.classgraph.ClassGraph import io.kotest.matchers.collections.shouldContainAll @@ -68,7 +67,7 @@ class WorkerBinderTest : BaseTest() { } @Singleton -@Component(modules = [AssistedInjectModule::class, WorkerBinder::class, MockProvider::class]) +@Component(modules = [WorkerBinder::class, MockProvider::class]) interface WorkerTestComponent { val factories: @JvmSuppressWildcards Map<Class<out ListenableWorker>, Provider<InjectedWorkerFactory<out ListenableWorker>>> diff --git a/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt b/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt index 68d1ba19c174d4bb013eb4bcba609566a31cafdf..74f4a86af0db8ce3580a86504f0601a5cbd61eda 100644 --- a/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt +++ b/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt @@ -9,6 +9,10 @@ import timber.log.Timber import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * If you have a test that uses a coroutine that never stops, you may use this. + */ + @ExperimentalCoroutinesApi // Since 1.2.1, tentatively till 1.3.0 fun TestCoroutineScope.runBlockingTest2( ignoreActive: Boolean = false, diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt index 4883ed9049aadf384f6dbd79469f450eb464397b..2e38ca455a29ba71aeae62febc1759557badd09c 100644 --- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt @@ -56,7 +56,7 @@ class DebugOptionsFragmentViewModelTest : BaseTest() { private fun createViewModel(): DebugOptionsFragmentViewModel = DebugOptionsFragmentViewModel( envSetup = environmentSetup, - dispatcherProvider = TestDispatcherProvider + dispatcherProvider = TestDispatcherProvider() ) @Test diff --git a/Corona-Warn-App/src/testShared/java/testhelpers/TestDispatcherProvider.kt b/Corona-Warn-App/src/testShared/java/testhelpers/TestDispatcherProvider.kt index 9b338acc7d85ffeeb83d5fd9d3b522de58e9a693..9aff497b970a49f8ac22e0ca24b3557745b9c663 100644 --- a/Corona-Warn-App/src/testShared/java/testhelpers/TestDispatcherProvider.kt +++ b/Corona-Warn-App/src/testShared/java/testhelpers/TestDispatcherProvider.kt @@ -1,18 +1,23 @@ package testhelpers import de.rki.coronawarnapp.util.coroutine.DispatcherProvider -import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext -object TestDispatcherProvider : DispatcherProvider { - override val Default: CoroutineDispatcher - get() = Dispatchers.Unconfined - override val Main: CoroutineDispatcher - get() = Dispatchers.Unconfined - override val MainImmediate: CoroutineDispatcher - get() = Dispatchers.Unconfined - override val Unconfined: CoroutineDispatcher - get() = Dispatchers.Unconfined - override val IO: CoroutineDispatcher - get() = Dispatchers.Unconfined +class TestDispatcherProvider(private val context: CoroutineContext? = null) : DispatcherProvider { + override val Default: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val Main: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val MainImmediate: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val Unconfined: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val IO: CoroutineContext + get() = context ?: Dispatchers.Unconfined } + +fun CoroutineScope.asDispatcherProvider() = this.coroutineContext.asDispatcherProvider() + +fun CoroutineContext.asDispatcherProvider() = TestDispatcherProvider(context = this)