From 8787d51d93efc99135dfbd914c563d4e69996a22 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Thu, 1 Jul 2021 14:19:08 +0200 Subject: [PATCH 01/17] Plumbing for the local incidence cards including layouts and item view holder --- .../ui/homecards/StatisticsCardAdapter.kt | 3 + .../ui/homecards/cards/LocalIncidenceCard.kt | 93 +++++ .../util/formatter/FormatterStatistics.kt | 3 +- .../ic_statistics_local_incidence.xml | 357 ++++++++++++++++++ .../ic_statistics_local_incidence.xml | 357 ++++++++++++++++++ ...tatistics_cards_local_incidence_layout.xml | 121 ++++++ .../menu/menu_statistics_local_incidence.xml | 9 + .../src/main/res/values-de/strings.xml | 10 + .../src/main/res/values/strings.xml | 11 + 9 files changed, 962 insertions(+), 2 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt create mode 100644 Corona-Warn-App/src/main/res/drawable-night/ic_statistics_local_incidence.xml create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_statistics_local_incidence.xml create mode 100644 Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml create mode 100644 Corona-Warn-App/src/main/res/menu/menu_statistics_local_incidence.xml 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 a26f0233ae2..5b4a5fbd128 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 @@ -8,6 +8,7 @@ import de.rki.coronawarnapp.statistics.AppliedVaccinationRatesStats import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.KeySubmissionsStats +import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedCompletelyStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedOnceStats import de.rki.coronawarnapp.statistics.SevenDayRValue @@ -17,6 +18,7 @@ import de.rki.coronawarnapp.statistics.ui.homecards.cards.AppliedVaccinationRate 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.LocalIncidenceCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.PersonsVaccinatedCompletelyCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.PersonsVaccinatedOnceCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.SevenDayRValueCard @@ -41,6 +43,7 @@ class StatisticsCardAdapter : DataBinderMod>(data), TypedVHCreatorMod({ data[it].stats is InfectionStats }) { InfectionsCard(it) }, TypedVHCreatorMod({ data[it].stats is IncidenceStats }) { IncidenceCard(it) }, + TypedVHCreatorMod({ data[it].stats is LocalIncidenceStats }) { LocalIncidenceCard(it) }, TypedVHCreatorMod({ data[it].stats is KeySubmissionsStats }) { KeySubmissionsCard(it) }, TypedVHCreatorMod({ data[it].stats is SevenDayRValue }) { SevenDayRValueCard(it) }, TypedVHCreatorMod({ data[it].stats is PersonsVaccinatedOnceStats }) { PersonsVaccinatedOnceCard(it) }, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt new file mode 100644 index 00000000000..4ff3475c8af --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -0,0 +1,93 @@ +package de.rki.coronawarnapp.statistics.ui.homecards.cards + +import android.view.ViewGroup +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.HomeStatisticsCardsLocalIncidenceLayoutBinding +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.LocalIncidenceStats +import de.rki.coronawarnapp.statistics.StatsItem +import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter +import de.rki.coronawarnapp.statistics.util.formatStatisticalValue +import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends +import de.rki.coronawarnapp.statistics.util.getLocalizedSpannableString +import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.BaseCheckInVH.Companion.setupMenu +import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithLineBreak +import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel + +class LocalIncidenceCard(parent: ViewGroup) : + StatisticsCardAdapter.ItemVH( + R.layout.home_statistics_cards_basecard_layout, + parent + ) { + + override val viewBinding = lazy { + HomeStatisticsCardsLocalIncidenceLayoutBinding.inflate( + layoutInflater, + itemView.findViewById(R.id.card_container), + true + ) + } + + override val onBindData: HomeStatisticsCardsLocalIncidenceLayoutBinding.( + item: StatisticsCardItem, + payloads: List + ) -> Unit = { item, _ -> + + overflowMenuButton.setupMenu(R.menu.menu_statistics_local_incidence) { + when (it.itemId) { + R.id.menu_information -> item.onClickListener(item.stats).let { true } + R.id.menu_remove_item -> item.onClickListener(item.stats).let { true } + else -> false + } + } + + with(item.stats as LocalIncidenceStats) { + incidenceContainer.contentDescription = + buildAccessibilityStringForLocalIncidenceCard(item.stats, sevenDayIncidence) + + locationLabel.text = federalState.name + + primaryLabel.text = getPrimaryLabel(context) + primaryValue.text = getLocalizedSpannableString( + context, + formatStatisticalValue(context, sevenDayIncidence.value, sevenDayIncidence.decimals) + ) + + primaryValue.contentDescription = StringBuilder() + .appendWithTrailingSpace(context.getString(R.string.statistics_card_local_incidence_title)) + .appendWithTrailingSpace(getPrimaryLabel(context)) + .appendWithTrailingSpace( + formatStatisticalValue( + context, + sevenDayIncidence.value, + sevenDayIncidence.decimals + ) + ) + .append(getContentDescriptionForTrends(context, sevenDayIncidence.trend)) + + trendArrow.setTrend(sevenDayIncidence.trend, sevenDayIncidence.trendSemantic) + } + } + + private fun buildAccessibilityStringForLocalIncidenceCard( + item: StatsItem, + sevenDayIncidence: KeyFigureCardOuterClass.KeyFigure + ): StringBuilder { + + return StringBuilder() + .appendWithTrailingSpace(context.getString(R.string.accessibility_statistics_card_announcement)) + .appendWithLineBreak(context.getString(R.string.statistics_card_local_incidence_title)) + .appendWithTrailingSpace(item.getPrimaryLabel(context)) + .appendWithTrailingSpace( + formatStatisticalValue( + context, + sevenDayIncidence.value, + sevenDayIncidence.decimals + ) + ) + .appendWithTrailingSpace(context.getString(R.string.statistics_card_local_incidence_value_description)) + .appendWithLineBreak(getContentDescriptionForTrends(context, sevenDayIncidence.trend)) + .append(context.getString(R.string.accessibility_statistics_card_navigation_information)) + } +} 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 index 2aa8e0b43e3..97e2bd807ff 100644 --- 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 @@ -33,7 +33,7 @@ fun StatsItem.getPrimaryLabel(context: Context): String { yesterday -> context.getString(R.string.statistics_primary_value_yesterday) else -> dateTimeFormatter.print(updatedAtDate) } - is IncidenceStats -> when (updatedAtDate) { + is IncidenceStats, is LocalIncidenceStats -> when (updatedAtDate) { 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(updatedAtDate)) @@ -58,6 +58,5 @@ fun StatsItem.getPrimaryLabel(context: Context): String { yesterday -> context.getString(R.string.statistics_primary_value_yesterday) else -> context.getString(R.string.statistics_primary_value_until, dateTimeFormatter.print(updatedAtDate)) } - is LocalIncidenceStats -> "" // TODO No ui code done yet } } diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_local_incidence.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_local_incidence.xml new file mode 100644 index 00000000000..20550d62a45 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable-night/ic_statistics_local_incidence.xml @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_statistics_local_incidence.xml b/Corona-Warn-App/src/main/res/drawable/ic_statistics_local_incidence.xml new file mode 100644 index 00000000000..a45d1d30391 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_statistics_local_incidence.xml @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml new file mode 100644 index 00000000000..8a0ad9a5e15 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/menu/menu_statistics_local_incidence.xml b/Corona-Warn-App/src/main/res/menu/menu_statistics_local_incidence.xml new file mode 100644 index 00000000000..6e38d681ead --- /dev/null +++ b/Corona-Warn-App/src/main/res/menu/menu_statistics_local_incidence.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 97c0a02757a..dd9122a3de0 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -1515,6 +1515,16 @@ "bestätigte Neuinfektionen je 100.000 Einwohner" + + + "Lokale 7-Tage-Inzidenz" + + "bestätigte Neuinfektionen je 100.000 Einwohner" + + "Information" + + "Entfernen" + "Bestätigte Neuinfektionen" diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 4941f5f2e27..17fef9c083e 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -1515,6 +1515,17 @@ "confirmed new infections per 100,000 residents" + + + + + + + + + + + "Confirmed New Infections" From ccf5e9fb54adf72dec915cac2c09f72aa6f5259b Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Sun, 11 Jul 2021 17:35:21 +0200 Subject: [PATCH 02/17] added support for addition and removal of local statistics homescreen cards, district names are resolved in the parser, also added fetching of local statistics based on active districts --- .../analytics/common/Districts.kt | 3 +- .../rki/coronawarnapp/statistics/StatsItem.kt | 29 ++++++++--- .../local/FederalStateToPackageId.kt | 7 ++- .../local/source/LocalStatisticsParser.kt | 50 +++++++++---------- .../local/source/LocalStatisticsProvider.kt | 32 ++++++++---- .../LocalStatisticsRetrievalScheduler.kt | 5 +- .../storage/LocalStatisticsConfigStorage.kt | 23 +++++++-- .../ui/homecards/StatisticsHomeCard.kt | 6 ++- .../ui/homecards/cards/LocalIncidenceCard.kt | 16 +++--- .../ui/homecards/cards/StatisticsCardItem.kt | 4 +- .../FederalStateSelectionViewModel.kt | 10 ++-- .../ui/main/home/HomeFragmentViewModel.kt | 21 +++++++- 12 files changed, 137 insertions(+), 69 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Districts.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Districts.kt index f7d743ac17a..8a13edd29ba 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Districts.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/Districts.kt @@ -15,8 +15,7 @@ class Districts @Inject constructor( @AppContext private val context: Context, @BaseGson private val gson: Gson ) { - - suspend fun loadDistricts(): List { + fun loadDistricts(): List { return try { val rawDistricts = context.assets.open(ASSET_NAME).bufferedReader().use { it.readText() } gson.fromJson(rawDistricts) 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 33f1eeb92ab..ce2230d274b 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,7 +1,7 @@ package de.rki.coronawarnapp.statistics import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure -import de.rki.coronawarnapp.server.protocols.internal.stats.LocalStatisticsOuterClass +import de.rki.coronawarnapp.util.ui.LazyString import org.joda.time.Instant import timber.log.Timber @@ -12,12 +12,26 @@ data class StatisticsData( override fun toString(): String { return "StatisticsData(cards=${ - items.map { - when (it) { - is AddStatsItem -> "AddCard(${it.isEnabled})" - is StatsItem -> it.cardType.name + " " + it.updatedAt + items.map { + when (it) { + is AddStatsItem -> "AddCard(${it.isEnabled})" + is StatsItem -> it.cardType.name + " " + it.updatedAt + } + } + })" + } +} + +data class LocalStatisticsData( + val items: List = emptyList() +) { + val isDataAvailable: Boolean = items.isNotEmpty() + + override fun toString(): String { + return "StatisticsData(cards=${ + items.map { + it.cardType.name + " " + it.updatedAt } - } })" } } @@ -91,7 +105,8 @@ data class IncidenceStats( data class LocalIncidenceStats( override val updatedAt: Instant, override val keyFigures: List, - val federalState: LocalStatisticsOuterClass.FederalStateData.FederalState + val districtId: Int, + val districtName: LazyString, ) : StatsItem(cardType = Type.LOCAL_INCIDENCE) { val sevenDayIncidence: KeyFigure diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt index c75aaaf9a00..2400dff98bc 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt @@ -16,5 +16,10 @@ enum class FederalStateToPackageId(val packageId: Int) { SN(6), ST(6), SH(4), - TH(6) + TH(6); + + companion object { + fun getForName(name: String): FederalStateToPackageId? = + values().firstOrNull { it.name == name } + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt index 9a5ab1912d7..5f83927b630 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt @@ -1,50 +1,48 @@ package de.rki.coronawarnapp.statistics.local.source import dagger.Reusable +import de.rki.coronawarnapp.datadonation.analytics.common.Districts import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass import de.rki.coronawarnapp.server.protocols.internal.stats.LocalStatisticsOuterClass import de.rki.coronawarnapp.statistics.LocalIncidenceStats -import de.rki.coronawarnapp.statistics.StatisticsData +import de.rki.coronawarnapp.statistics.LocalStatisticsData +import de.rki.coronawarnapp.util.ui.toLazyString import org.joda.time.Instant import timber.log.Timber import javax.inject.Inject @Reusable -class LocalStatisticsParser @Inject constructor() { - - fun parse(rawData: ByteArray): StatisticsData { - val parsed = LocalStatisticsOuterClass.LocalStatistics.parseFrom(rawData) +class LocalStatisticsParser @Inject constructor( + private val districtSource: Districts +) { + private val districts by lazy { + districtSource.loadDistricts() + } - val mappedFederalStates = parsed.federalStateDataList.mapNotNull { rawState -> - try { - val updatedAt = Instant.ofEpochSecond(rawState.updatedAt) - val stateIncidenceKeyFigure = rawState.sevenDayIncidence.toKeyFigure() + private fun getNameForId(districtId: Int) = + districts.first { it.districtId == districtId }.districtName.toLazyString() - LocalIncidenceStats( - updatedAt = updatedAt, - keyFigures = listOf(stateIncidenceKeyFigure), - federalState = rawState.federalState - ).also { - Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) - it.requireValidity() - } - } catch (e: Exception) { - Timber.tag(TAG).e("Failed to parse raw federal state: %s", rawState) - null - } - } + fun parse(rawData: ByteArray): LocalStatisticsData { + val parsed = LocalStatisticsOuterClass.LocalStatistics.parseFrom(rawData) val mappedAdministrativeUnit = parsed.administrativeUnitDataList.mapNotNull { rawState -> try { val updatedAt = Instant.ofEpochSecond(rawState.updatedAt) val administrativeUnitIncidenceKeyFigure = rawState.sevenDayIncidence.toKeyFigure() - val federalStateId = rawState.administrativeUnitShortId.toString().dropLast(3).toInt() + val leftPaddedShortId = rawState.administrativeUnitShortId + .toString() + .padStart(5, '0') + + val districtId = "110${leftPaddedShortId}".toInt() + + val districtName = getNameForId(districtId) LocalIncidenceStats( updatedAt = updatedAt, keyFigures = listOf(administrativeUnitIncidenceKeyFigure), - federalState = LocalStatisticsOuterClass.FederalStateData.FederalState.forNumber(federalStateId) + districtId = districtId, + districtName = districtName, ).also { Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) it.requireValidity() @@ -55,9 +53,7 @@ class LocalStatisticsParser @Inject constructor() { } } - val mappedItems = mappedFederalStates + mappedAdministrativeUnit - - return StatisticsData(items = mappedItems).also { + return LocalStatisticsData(items = mappedAdministrativeUnit).also { Timber.tag(TAG).d("Parsed local statistics data, %d cards.", it.items.size) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt index ecb7b2d88de..7e36e807d8d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt @@ -1,6 +1,6 @@ package de.rki.coronawarnapp.statistics.local.source -import de.rki.coronawarnapp.statistics.StatisticsData +import de.rki.coronawarnapp.statistics.LocalStatisticsData import de.rki.coronawarnapp.statistics.local.FederalStateToPackageId import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import de.rki.coronawarnapp.util.coroutine.AppScope @@ -9,6 +9,8 @@ import de.rki.coronawarnapp.util.flow.HotDataFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import org.joda.time.Duration import timber.log.Timber import javax.inject.Inject @@ -41,12 +43,24 @@ class LocalStatisticsProvider @Inject constructor( } } - val current: Flow> = localStatisticsData.data + val current: Flow = localStatisticsData.data.map { + val groupedStats = it.reduceOrNull { acc, localStatisticsData -> + LocalStatisticsData(acc.items + localStatisticsData.items) + } ?: LocalStatisticsData() + + groupedStats.copy( + items = groupedStats.items.filter { + localStatisticsConfigStorage.activeDistricts.value.any { district -> + district.districtId == it.districtId + } + } + ) + } - private fun fetchCacheFirst(): List { + private suspend fun fetchCacheFirst(): List { Timber.tag(TAG).d("fromCache()") - val targetedStates = localStatisticsConfigStorage.activeStates.value + val targetedStates = localStatisticsConfigStorage.activeStates.first() val cacheResults = targetedStates.map { fromCache(it) } @@ -54,10 +68,10 @@ class LocalStatisticsProvider @Inject constructor( triggerUpdate() } - return cacheResults.map { it ?: StatisticsData() } + return cacheResults.map { it ?: LocalStatisticsData() } } - private fun fromCache(forState: FederalStateToPackageId): StatisticsData? = try { + private fun fromCache(forState: FederalStateToPackageId): LocalStatisticsData? = try { Timber.tag(TAG).d("fromCache(%s)", forState) localStatisticsCache.load(forState)?.let { localStatisticsParser.parse(it) }?.also { @@ -68,15 +82,15 @@ class LocalStatisticsProvider @Inject constructor( null } - private suspend fun fromServer(): List { + private suspend fun fromServer(): List { Timber.tag(TAG).d("fromServer()") - val targetedStates = localStatisticsConfigStorage.activeStates.value + val targetedStates = localStatisticsConfigStorage.activeStates.first() return targetedStates.map { fromServer(it) } } - private suspend fun fromServer(forState: FederalStateToPackageId): StatisticsData { + private suspend fun fromServer(forState: FederalStateToPackageId): LocalStatisticsData { Timber.tag(TAG).d("fromServer(%s)", forState) val rawData = server.getRawLocalStatistics(forState) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsRetrievalScheduler.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsRetrievalScheduler.kt index 9c6c303c75e..5b8147f3b15 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsRetrievalScheduler.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsRetrievalScheduler.kt @@ -6,7 +6,6 @@ import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.device.ForegroundState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -24,7 +23,7 @@ class LocalStatisticsRetrievalScheduler @Inject constructor( private val updateStatsTrigger = combine( foregroundState.isInForeground, - localStatisticsConfigStorage.activeStates.flow + localStatisticsConfigStorage.activeStates ) { isInForeground, activeStates -> val statsChanged = !lastActiveStates.containsAll(activeStates) lastActiveStates.clear() @@ -36,7 +35,7 @@ class LocalStatisticsRetrievalScheduler @Inject constructor( statsChanged ) isInForeground || statsChanged - }.distinctUntilChanged() + } fun setup() { Timber.tag(TAG).i("setup()") diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt index 69d2c907ce6..9abeeaaf2be 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt @@ -1,23 +1,40 @@ package de.rki.coronawarnapp.statistics.local.storage import android.content.Context +import com.google.gson.Gson +import de.rki.coronawarnapp.datadonation.analytics.common.Districts import de.rki.coronawarnapp.statistics.local.FederalStateToPackageId import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.preferences.FlowPreference import de.rki.coronawarnapp.util.preferences.createFlowPreference +import de.rki.coronawarnapp.util.serialization.BaseGson +import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Singleton @Singleton class LocalStatisticsConfigStorage @Inject constructor( - @AppContext val context: Context + @AppContext val context: Context, + @BaseGson private val gson: Gson, ) { private val prefs by lazy { context.getSharedPreferences("statistics_local_config", Context.MODE_PRIVATE) } - val activeStates = prefs.createFlowPreference(PKEY_ACTIVE_STATES, emptyList()) + val activeDistricts = prefs.createFlowPreference( + key = PKEY_ACTIVE_DISTRICTS, + reader = FlowPreference.gsonReader(gson, emptySet()), + writer = FlowPreference.gsonWriter(gson) + ) + + val activeStates = activeDistricts.flow + .map { + it.map { district -> district.federalStateShortName } + .mapNotNull { stateId -> FederalStateToPackageId.getForName(stateId) } + .distinct() + } companion object { - private const val PKEY_ACTIVE_STATES = "statistics.local.states" + private const val PKEY_ACTIVE_DISTRICTS = "statistics.local.districts" } } 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 713092c56b6..4a4e039a919 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 @@ -9,6 +9,7 @@ import androidx.recyclerview.widget.PagerSnapHelper import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsScrollcontainerBinding import de.rki.coronawarnapp.statistics.GenericStatsItem +import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.StatisticsData import de.rki.coronawarnapp.statistics.ui.homecards.cards.StatisticsCardItem import de.rki.coronawarnapp.ui.main.home.HomeAdapter @@ -59,7 +60,7 @@ class StatisticsHomeCard( savedStateKey = "stats:${item.stableId}" item.data.items.map { - StatisticsCardItem(it, item.onClickListener) + StatisticsCardItem(it, item.onClickListener, item.onRemoveListener) }.let { statisticsCardAdapter.update(it) } @@ -77,7 +78,8 @@ class StatisticsHomeCard( data class Item( val data: StatisticsData, - val onClickListener: (GenericStatsItem) -> Unit + val onClickListener: (GenericStatsItem) -> Unit, + val onRemoveListener: (LocalIncidenceStats) -> Unit, ) : HomeItem { override val stableId: Long = Item::class.java.name.hashCode().toLong() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt index 4ff3475c8af..18b92a256f4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -33,20 +33,20 @@ class LocalIncidenceCard(parent: ViewGroup) : item: StatisticsCardItem, payloads: List ) -> Unit = { item, _ -> + with(item.stats as LocalIncidenceStats) { - overflowMenuButton.setupMenu(R.menu.menu_statistics_local_incidence) { - when (it.itemId) { - R.id.menu_information -> item.onClickListener(item.stats).let { true } - R.id.menu_remove_item -> item.onClickListener(item.stats).let { true } - else -> false + overflowMenuButton.setupMenu(R.menu.menu_statistics_local_incidence) { + when (it.itemId) { + R.id.menu_information -> item.onClickListener(item.stats).let { true } + R.id.menu_remove_item -> item.onRemoveListener(item.stats).let { true } + else -> false + } } - } - with(item.stats as LocalIncidenceStats) { incidenceContainer.contentDescription = buildAccessibilityStringForLocalIncidenceCard(item.stats, sevenDayIncidence) - locationLabel.text = federalState.name + locationLabel.text = districtName.get(context) primaryLabel.text = getPrimaryLabel(context) primaryValue.text = getLocalizedSpannableString( 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 36b398f2987..f3e4fa340d5 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 @@ -2,12 +2,14 @@ package de.rki.coronawarnapp.statistics.ui.homecards.cards import de.rki.coronawarnapp.statistics.AddStatsItem import de.rki.coronawarnapp.statistics.GenericStatsItem +import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.util.lists.HasStableId data class StatisticsCardItem( val stats: GenericStatsItem, - val onClickListener: (GenericStatsItem) -> Unit + val onClickListener: (GenericStatsItem) -> Unit, + val onRemoveListener: (LocalIncidenceStats) -> Unit, ) : HasStableId { override val stableId: Long = when (stats) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt index fb7cf197d1a..1beb7c25fa1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt @@ -10,6 +10,7 @@ import de.rki.coronawarnapp.datadonation.analytics.common.Districts import de.rki.coronawarnapp.datadonation.analytics.common.federalStateShortName import de.rki.coronawarnapp.datadonation.analytics.common.labelStringRes import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData +import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.ui.toLazyString @@ -27,7 +28,8 @@ import timber.log.Timber class FederalStateSelectionViewModel @AssistedInject constructor( @Assisted val selectedFederalStateShortName: String?, @AppContext private val context: Context, - private val districtsSource: Districts + private val districtsSource: Districts, + private val localStatisticsConfigStorage: LocalStatisticsConfigStorage, ) : CWAViewModel() { private val federalStateSource: Flow> = flowOf(PpaData.PPAFederalState.values()) @@ -72,9 +74,9 @@ class FederalStateSelectionViewModel @AssistedInject constructor( event.postValue(Events.OpenDistricts(item.data.federalStateShortName)) } is Districts.District -> { - - // TODO: use data in (EXPOSUREAPP-7446) - + localStatisticsConfigStorage.activeDistricts.update { districts -> + districts + item.data + } event.postValue(Events.FinishEvent) } else -> throw IllegalArgumentException() 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 3649b73778a..ce1dc94c6ce 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 @@ -24,6 +24,8 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.core.CovidCertificateSe import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.statistics.AddStatsItem +import de.rki.coronawarnapp.statistics.local.source.LocalStatisticsProvider +import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import de.rki.coronawarnapp.statistics.source.StatisticsProvider import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard import de.rki.coronawarnapp.storage.TracingRepository @@ -89,6 +91,7 @@ class HomeFragmentViewModel @AssistedInject constructor( tracingStateProviderFactory: TracingStateProvider.Factory, coronaTestRepository: CoronaTestRepository, statisticsProvider: StatisticsProvider, + localStatisticsProvider: LocalStatisticsProvider, vaccinationRepository: VaccinationRepository, private val errorResetTool: EncryptionErrorResetTool, private val tracingRepository: TracingRepository, @@ -101,6 +104,7 @@ class HomeFragmentViewModel @AssistedInject constructor( private val timeStamper: TimeStamper, private val bluetoothSupport: BluetoothSupport, private val covidCertificateSettings: CovidCertificateSettings, + private val localStatisticsConfigStorage: LocalStatisticsConfigStorage, ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { private var isLoweredRiskLevelDialogBeingShown = false @@ -130,11 +134,20 @@ class HomeFragmentViewModel @AssistedInject constructor( } } + private val combinedStatistics = combine( + statisticsProvider.current, + localStatisticsProvider.current + ) { statsData, localStatsData -> + statsData.copy( + items = localStatsData.items + statsData.items + ) + } + val homeItems: LiveData> = combine( tracingCardItems, coronaTestRepository.latestPCRT, coronaTestRepository.latestRAT, - statisticsProvider.current.distinctUntilChanged(), + combinedStatistics, appConfigProvider.currentConfig.map { it.coronaTestParameters }.distinctUntilChanged(), vaccinationRepository.vaccinationInfos ) { tracingItem, testPCR, testRAT, statsData, coronaTestParameters, vaccinatedPersons -> @@ -187,7 +200,6 @@ class HomeFragmentViewModel @AssistedInject constructor( if (statsData.isDataAvailable) { add( StatisticsHomeCard.Item( - // TODO: improve in future PRs (EXPOSUREAPP-7446) isEnable depends on number of Local Cards data = statsData.copy(items = mutableListOf(AddStatsItem(true)).plus(statsData.items)), onClickListener = { when (it) { @@ -196,6 +208,11 @@ class HomeFragmentViewModel @AssistedInject constructor( } else -> events.postValue(HomeFragmentEvents.GoToStatisticsExplanation) } + }, + onRemoveListener = { + localStatisticsConfigStorage.activeDistricts.update { districts -> + districts.filter { district -> district.districtId != it.districtId }.toSet() + } } ) ) From d396a8267901fd5eec6ea75a633c4ffdc392b844 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Sun, 11 Jul 2021 17:42:39 +0200 Subject: [PATCH 03/17] Linting --- .../de/rki/coronawarnapp/statistics/StatsItem.kt | 16 ++++++++-------- .../local/source/LocalStatisticsParser.kt | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) 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 ce2230d274b..33d742ead7b 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 @@ -12,12 +12,12 @@ data class StatisticsData( override fun toString(): String { return "StatisticsData(cards=${ - items.map { - when (it) { - is AddStatsItem -> "AddCard(${it.isEnabled})" - is StatsItem -> it.cardType.name + " " + it.updatedAt - } + items.map { + when (it) { + is AddStatsItem -> "AddCard(${it.isEnabled})" + is StatsItem -> it.cardType.name + " " + it.updatedAt } + } })" } } @@ -29,9 +29,9 @@ data class LocalStatisticsData( override fun toString(): String { return "StatisticsData(cards=${ - items.map { - it.cardType.name + " " + it.updatedAt - } + items.map { + it.cardType.name + " " + it.updatedAt + } })" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt index 5f83927b630..b0a6a70db9b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt @@ -34,7 +34,7 @@ class LocalStatisticsParser @Inject constructor( .toString() .padStart(5, '0') - val districtId = "110${leftPaddedShortId}".toInt() + val districtId = "110$leftPaddedShortId".toInt() val districtName = getNameForId(districtId) From 2cbe535fc22cf961336959f38a869eb2785d612e Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Sun, 11 Jul 2021 18:23:42 +0200 Subject: [PATCH 04/17] Fixed unittests --- .../statistics/ui/homecards/StatisticsHomeCard.kt | 2 +- .../main/home/HomeFragmentViewModelTest.kt | 10 +++++++++- .../ui/homecards/StatisticsHomeCardItemTest.kt | 8 ++++---- 3 files changed, 14 insertions(+), 6 deletions(-) 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 395e13a164b..cb069830534 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 @@ -83,7 +83,7 @@ class StatisticsHomeCard( data class Item( val data: StatisticsData, val onClickListener: (GenericStatsItem) -> Unit, - val onRemoveListener: (LocalIncidenceStats) -> Unit, + val onRemoveListener: (LocalIncidenceStats) -> Unit = {}, ) : HomeItem { override val stableId: Long = Item::class.java.name.hashCode().toLong() 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 32c191b11a0..d080a0b7b78 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 @@ -7,6 +7,8 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.core.CovidCertificateSe import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.statistics.local.source.LocalStatisticsProvider +import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import de.rki.coronawarnapp.statistics.source.StatisticsProvider import de.rki.coronawarnapp.storage.TracingRepository import de.rki.coronawarnapp.storage.TracingSettings @@ -59,6 +61,7 @@ class HomeFragmentViewModelTest : BaseTest() { @MockK lateinit var cwaSettings: CWASettings @MockK lateinit var appConfigProvider: AppConfigProvider @MockK lateinit var statisticsProvider: StatisticsProvider + @MockK lateinit var localStatisticsProvider: LocalStatisticsProvider @MockK lateinit var appShortcutsHelper: AppShortcutsHelper @MockK lateinit var tracingSettings: TracingSettings @MockK lateinit var traceLocationOrganizerSettings: TraceLocationOrganizerSettings @@ -66,6 +69,7 @@ class HomeFragmentViewModelTest : BaseTest() { @MockK lateinit var bluetoothSupport: BluetoothSupport @MockK lateinit var covidCertificateSettings: CovidCertificateSettings @MockK lateinit var vaccinationRepository: VaccinationRepository + @MockK lateinit var localStatisticsConfigStorage: LocalStatisticsConfigStorage @BeforeEach fun setup() { @@ -83,6 +87,8 @@ class HomeFragmentViewModelTest : BaseTest() { coEvery { appConfigProvider.currentConfig } returns emptyFlow() coEvery { statisticsProvider.current } returns emptyFlow() + coEvery { localStatisticsProvider.current } returns emptyFlow() + every { timeStamper.nowUTC } returns Instant.ofEpochMilli(100101010) bluetoothSupport.apply { @@ -102,13 +108,15 @@ class HomeFragmentViewModelTest : BaseTest() { cwaSettings = cwaSettings, appConfigProvider = appConfigProvider, statisticsProvider = statisticsProvider, + localStatisticsProvider = localStatisticsProvider, appShortcutsHelper = appShortcutsHelper, tracingSettings = tracingSettings, traceLocationOrganizerSettings = traceLocationOrganizerSettings, timeStamper = timeStamper, bluetoothSupport = bluetoothSupport, vaccinationRepository = vaccinationRepository, - covidCertificateSettings = covidCertificateSettings + covidCertificateSettings = covidCertificateSettings, + localStatisticsConfigStorage = localStatisticsConfigStorage ) @Test 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 index 854a6f74e5e..67066a505ba 100644 --- 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 @@ -10,13 +10,13 @@ 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())) { + val itemWithClickListener1 = StatisticsHomeCard.Item(StatisticsData(emptyList()), { // ClickListener1 - } + }) - val itemWithClickListener2 = StatisticsHomeCard.Item(StatisticsData(emptyList())) { + val itemWithClickListener2 = StatisticsHomeCard.Item(StatisticsData(emptyList()), { // ClickListener2 - } + }) // Check if click listeners are actually different if (itemWithClickListener1.onClickListener == itemWithClickListener2.onClickListener) { From f346806ea5332f04bcb8c0a8255d952ffee01979 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Sun, 11 Jul 2021 18:26:42 +0200 Subject: [PATCH 05/17] Removed unused layout tag --- ...tatistics_cards_local_incidence_layout.xml | 204 +++++++++--------- 1 file changed, 100 insertions(+), 104 deletions(-) diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml index 8a0ad9a5e15..6f787e92853 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml @@ -1,121 +1,117 @@ - + android:id="@+id/incidence_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:ignore="MissingConstraints" + tools:layout_height="wrap_content" + tools:layout_width="@dimen/statistics_card_width"> - - - + android:contentDescription="@null" + android:importantForAccessibility="no" + android:paddingStart="0dp" + android:paddingEnd="@dimen/spacing_small" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/flow_layout" + app:srcCompat="@drawable/ic_statistics_local_incidence" /> - + - + - + - + - + - + - + - + - + - + - + - \ No newline at end of file From 97ef5a3d5c8eff6e6d7901ec88898c80f2ab9dd1 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 09:46:08 +0200 Subject: [PATCH 06/17] linting --- .../homecards/StatisticsHomeCardItemTest.kt | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 index 67066a505ba..206d5a55469 100644 --- 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 @@ -10,13 +10,19 @@ 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 - }) + val itemWithClickListener1 = StatisticsHomeCard.Item( + StatisticsData(emptyList()), + { + // ClickListener1 + } + ) + + val itemWithClickListener2 = StatisticsHomeCard.Item( + StatisticsData(emptyList()), + { + // ClickListener2 + } + ) // Check if click listeners are actually different if (itemWithClickListener1.onClickListener == itemWithClickListener2.onClickListener) { From 26c634147f2b204467ecb24025df3f43e555e0ec Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 14:27:01 +0200 Subject: [PATCH 07/17] Disabled local stats add card after 5 local stats are displayed --- .../rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 ce1dc94c6ce..19b7966dc57 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 @@ -139,7 +139,9 @@ class HomeFragmentViewModel @AssistedInject constructor( localStatisticsProvider.current ) { statsData, localStatsData -> statsData.copy( - items = localStatsData.items + statsData.items + items = mutableListOf(AddStatsItem(localStatsData.items.size < 5)) + + localStatsData.items + + statsData.items ) } @@ -200,7 +202,7 @@ class HomeFragmentViewModel @AssistedInject constructor( if (statsData.isDataAvailable) { add( StatisticsHomeCard.Item( - data = statsData.copy(items = mutableListOf(AddStatsItem(true)).plus(statsData.items)), + data = statsData, onClickListener = { when (it) { is AddStatsItem -> { From c84e60c79a280c783e4dea7d7aad2ab0edde2fb9 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 14:49:31 +0200 Subject: [PATCH 08/17] Storing SelectedDistrict instead of District allows us to match the user selection to a server result and enables us to sort the cards by the time they were added at --- .../rki/coronawarnapp/statistics/StatsItem.kt | 5 ++--- .../local/source/LocalStatisticsParser.kt | 19 ++++++------------- .../local/source/LocalStatisticsProvider.kt | 6 +++--- .../storage/LocalStatisticsConfigStorage.kt | 5 ++--- .../local/storage/SelectedDistrict.kt | 9 +++++++++ .../ui/homecards/cards/LocalIncidenceCard.kt | 2 +- .../FederalStateSelectionViewModel.kt | 5 ++++- .../ui/main/home/HomeFragmentViewModel.kt | 4 +++- 8 files changed, 30 insertions(+), 25 deletions(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/SelectedDistrict.kt 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 33d742ead7b..2b4d16e9d70 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,7 +1,7 @@ package de.rki.coronawarnapp.statistics import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure -import de.rki.coronawarnapp.util.ui.LazyString +import de.rki.coronawarnapp.statistics.local.storage.SelectedDistrict import org.joda.time.Instant import timber.log.Timber @@ -105,8 +105,7 @@ data class IncidenceStats( data class LocalIncidenceStats( override val updatedAt: Instant, override val keyFigures: List, - val districtId: Int, - val districtName: LazyString, + val selectedDistrict: SelectedDistrict?, ) : StatsItem(cardType = Type.LOCAL_INCIDENCE) { val sevenDayIncidence: KeyFigure diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt index b0a6a70db9b..1e0220dde20 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt @@ -1,27 +1,19 @@ package de.rki.coronawarnapp.statistics.local.source import dagger.Reusable -import de.rki.coronawarnapp.datadonation.analytics.common.Districts import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass import de.rki.coronawarnapp.server.protocols.internal.stats.LocalStatisticsOuterClass import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.LocalStatisticsData -import de.rki.coronawarnapp.util.ui.toLazyString +import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import org.joda.time.Instant import timber.log.Timber import javax.inject.Inject @Reusable class LocalStatisticsParser @Inject constructor( - private val districtSource: Districts + private val localStatisticsConfigStorage: LocalStatisticsConfigStorage, ) { - private val districts by lazy { - districtSource.loadDistricts() - } - - private fun getNameForId(districtId: Int) = - districts.first { it.districtId == districtId }.districtName.toLazyString() - fun parse(rawData: ByteArray): LocalStatisticsData { val parsed = LocalStatisticsOuterClass.LocalStatistics.parseFrom(rawData) @@ -36,13 +28,14 @@ class LocalStatisticsParser @Inject constructor( val districtId = "110$leftPaddedShortId".toInt() - val districtName = getNameForId(districtId) + val selectedDistrict = localStatisticsConfigStorage.activeDistricts.value.firstOrNull { + it.district.districtId == districtId + } LocalIncidenceStats( updatedAt = updatedAt, keyFigures = listOf(administrativeUnitIncidenceKeyFigure), - districtId = districtId, - districtName = districtName, + selectedDistrict = selectedDistrict ).also { Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) it.requireValidity() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt index 7e36e807d8d..22f62b06f3b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt @@ -50,10 +50,10 @@ class LocalStatisticsProvider @Inject constructor( groupedStats.copy( items = groupedStats.items.filter { - localStatisticsConfigStorage.activeDistricts.value.any { district -> - district.districtId == it.districtId + localStatisticsConfigStorage.activeDistricts.value.any { selected -> + selected.district.districtId == it.selectedDistrict?.district?.districtId } - } + }.sortedBy { selected -> selected.selectedDistrict?.addedAt }.reversed() ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt index 9abeeaaf2be..13cd6a6b8d0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt @@ -2,7 +2,6 @@ package de.rki.coronawarnapp.statistics.local.storage import android.content.Context import com.google.gson.Gson -import de.rki.coronawarnapp.datadonation.analytics.common.Districts import de.rki.coronawarnapp.statistics.local.FederalStateToPackageId import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.preferences.FlowPreference @@ -23,13 +22,13 @@ class LocalStatisticsConfigStorage @Inject constructor( val activeDistricts = prefs.createFlowPreference( key = PKEY_ACTIVE_DISTRICTS, - reader = FlowPreference.gsonReader(gson, emptySet()), + reader = FlowPreference.gsonReader(gson, emptySet()), writer = FlowPreference.gsonWriter(gson) ) val activeStates = activeDistricts.flow .map { - it.map { district -> district.federalStateShortName } + it.map { district -> district.district.federalStateShortName } .mapNotNull { stateId -> FederalStateToPackageId.getForName(stateId) } .distinct() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/SelectedDistrict.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/SelectedDistrict.kt new file mode 100644 index 00000000000..8fae1f7a59b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/SelectedDistrict.kt @@ -0,0 +1,9 @@ +package de.rki.coronawarnapp.statistics.local.storage + +import de.rki.coronawarnapp.datadonation.analytics.common.Districts +import org.joda.time.Instant + +data class SelectedDistrict( + val district: Districts.District, + val addedAt: Instant, +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt index 18b92a256f4..5ab03f308e4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -46,7 +46,7 @@ class LocalIncidenceCard(parent: ViewGroup) : incidenceContainer.contentDescription = buildAccessibilityStringForLocalIncidenceCard(item.stats, sevenDayIncidence) - locationLabel.text = districtName.get(context) + locationLabel.text = selectedDistrict?.district?.districtName primaryLabel.text = getPrimaryLabel(context) primaryValue.text = getLocalizedSpannableString( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt index 1beb7c25fa1..51e2e6c4a6f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/stateselection/FederalStateSelectionViewModel.kt @@ -11,6 +11,8 @@ import de.rki.coronawarnapp.datadonation.analytics.common.federalStateShortName import de.rki.coronawarnapp.datadonation.analytics.common.labelStringRes import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage +import de.rki.coronawarnapp.statistics.local.storage.SelectedDistrict +import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.ui.toLazyString @@ -30,6 +32,7 @@ class FederalStateSelectionViewModel @AssistedInject constructor( @AppContext private val context: Context, private val districtsSource: Districts, private val localStatisticsConfigStorage: LocalStatisticsConfigStorage, + private val timeStamper: TimeStamper ) : CWAViewModel() { private val federalStateSource: Flow> = flowOf(PpaData.PPAFederalState.values()) @@ -75,7 +78,7 @@ class FederalStateSelectionViewModel @AssistedInject constructor( } is Districts.District -> { localStatisticsConfigStorage.activeDistricts.update { districts -> - districts + item.data + districts + SelectedDistrict(item.data, timeStamper.nowUTC) } event.postValue(Events.FinishEvent) } 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 19b7966dc57..59c882d4000 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 @@ -213,7 +213,9 @@ class HomeFragmentViewModel @AssistedInject constructor( }, onRemoveListener = { localStatisticsConfigStorage.activeDistricts.update { districts -> - districts.filter { district -> district.districtId != it.districtId }.toSet() + districts.filter { selected -> + selected.district.districtId != it.selectedDistrict?.district?.districtId + }.toSet() } } ) From cb28845be4e878651e5e8f8e77e30cc7b2449d6c Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 14:52:38 +0200 Subject: [PATCH 09/17] Using the district id as stable id for local incidence cards --- .../statistics/ui/homecards/cards/StatisticsCardItem.kt | 1 + 1 file changed, 1 insertion(+) 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 f3e4fa340d5..7fee24f3cb4 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 @@ -15,5 +15,6 @@ data class StatisticsCardItem( override val stableId: Long = when (stats) { is AddStatsItem -> 0L is StatsItem -> stats.cardType.id.toLong() + is LocalIncidenceStats -> stats.selectedDistrict?.district?.districtId?.toLong() ?: 0L } } From b03a0d19bde65e2ad6993f1ec4fc8cbb4431215e Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 14:55:02 +0200 Subject: [PATCH 10/17] linting --- .../rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 59c882d4000..7b0a197c13e 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 @@ -139,9 +139,9 @@ class HomeFragmentViewModel @AssistedInject constructor( localStatisticsProvider.current ) { statsData, localStatsData -> statsData.copy( - items = mutableListOf(AddStatsItem(localStatsData.items.size < 5)) - + localStatsData.items - + statsData.items + items = mutableListOf(AddStatsItem(localStatsData.items.size < 5)) + + localStatsData.items + + statsData.items ) } From 7c554785a7c4b5a47631247fb10abffbe60a3e7e Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Mon, 12 Jul 2021 16:35:15 +0200 Subject: [PATCH 11/17] Hopefully this fixes the recycler view crashes --- .../statistics/ui/homecards/cards/StatisticsCardItem.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 7fee24f3cb4..55494854be3 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 @@ -14,7 +14,12 @@ data class StatisticsCardItem( override val stableId: Long = when (stats) { is AddStatsItem -> 0L - is StatsItem -> stats.cardType.id.toLong() - is LocalIncidenceStats -> stats.selectedDistrict?.district?.districtId?.toLong() ?: 0L + is StatsItem -> { + if (stats is LocalIncidenceStats) { + stats.selectedDistrict?.district?.districtId?.toLong() ?: 0L + } else { + stats.cardType.id.toLong() + } + } } } From b30055f09b0754d0c6ca63d650b64f3a45bb0e75 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Tue, 13 Jul 2021 10:11:27 +0200 Subject: [PATCH 12/17] First round of addressing comments --- .../rki/coronawarnapp/statistics/StatsItem.kt | 2 +- .../local/source/LocalStatisticsParser.kt | 22 +++++++++++++------ .../local/source/LocalStatisticsProvider.kt | 12 +++++----- .../storage/LocalStatisticsConfigStorage.kt | 13 ++++++++--- .../ui/homecards/cards/LocalIncidenceCard.kt | 2 +- .../ui/homecards/cards/StatisticsCardItem.kt | 4 ++-- .../ui/main/home/HomeFragmentViewModel.kt | 2 +- 7 files changed, 35 insertions(+), 22 deletions(-) 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 2b4d16e9d70..5b54fc37b38 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 @@ -105,7 +105,7 @@ data class IncidenceStats( data class LocalIncidenceStats( override val updatedAt: Instant, override val keyFigures: List, - val selectedDistrict: SelectedDistrict?, + val selectedDistrict: SelectedDistrict, ) : StatsItem(cardType = Type.LOCAL_INCIDENCE) { val sevenDayIncidence: KeyFigure diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt index 1e0220dde20..40ae9ac2861 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt @@ -32,13 +32,21 @@ class LocalStatisticsParser @Inject constructor( it.district.districtId == districtId } - LocalIncidenceStats( - updatedAt = updatedAt, - keyFigures = listOf(administrativeUnitIncidenceKeyFigure), - selectedDistrict = selectedDistrict - ).also { - Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) - it.requireValidity() + if (selectedDistrict != null) { + LocalIncidenceStats( + updatedAt = updatedAt, + keyFigures = listOf(administrativeUnitIncidenceKeyFigure), + selectedDistrict = selectedDistrict + ).also { + Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) + it.requireValidity() + } + } else { + Timber.tag(TAG).v( + "Failed to match au with id %s to user selected cards, this is probably not an error", + rawState.administrativeUnitShortId + ) + null } } catch (e: Exception) { Timber.tag(TAG).e("Failed to parse raw federal state: %s", rawState) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt index 22f62b06f3b..c75c47730c7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt @@ -43,17 +43,15 @@ class LocalStatisticsProvider @Inject constructor( } } - val current: Flow = localStatisticsData.data.map { - val groupedStats = it.reduceOrNull { acc, localStatisticsData -> + val current: Flow = localStatisticsData.data.map { localStatsList -> + val groupedStats = localStatsList.reduceOrNull { acc, localStatisticsData -> LocalStatisticsData(acc.items + localStatisticsData.items) } ?: LocalStatisticsData() groupedStats.copy( - items = groupedStats.items.filter { - localStatisticsConfigStorage.activeDistricts.value.any { selected -> - selected.district.districtId == it.selectedDistrict?.district?.districtId - } - }.sortedBy { selected -> selected.selectedDistrict?.addedAt }.reversed() + items = groupedStats.items + .sortedBy { selected -> selected.selectedDistrict.addedAt } + .reversed() ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt index 13cd6a6b8d0..aa2bdc622bf 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/storage/LocalStatisticsConfigStorage.kt @@ -8,6 +8,7 @@ import de.rki.coronawarnapp.util.preferences.FlowPreference import de.rki.coronawarnapp.util.preferences.createFlowPreference import de.rki.coronawarnapp.util.serialization.BaseGson import kotlinx.coroutines.flow.map +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -27,9 +28,15 @@ class LocalStatisticsConfigStorage @Inject constructor( ) val activeStates = activeDistricts.flow - .map { - it.map { district -> district.district.federalStateShortName } - .mapNotNull { stateId -> FederalStateToPackageId.getForName(stateId) } + .map { districtFlow -> + districtFlow.map { it.district.federalStateShortName } + .mapNotNull { stateName -> + FederalStateToPackageId.getForName(stateName).also { + if (it == null) { + Timber.e("Failed to map federal state short name to id %s", stateName) + } + } + } .distinct() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt index 5ab03f308e4..bd980f005e1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -46,7 +46,7 @@ class LocalIncidenceCard(parent: ViewGroup) : incidenceContainer.contentDescription = buildAccessibilityStringForLocalIncidenceCard(item.stats, sevenDayIncidence) - locationLabel.text = selectedDistrict?.district?.districtName + locationLabel.text = selectedDistrict.district.districtName primaryLabel.text = getPrimaryLabel(context) primaryValue.text = getLocalizedSpannableString( 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 55494854be3..31298f2da47 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 @@ -13,10 +13,10 @@ data class StatisticsCardItem( ) : HasStableId { override val stableId: Long = when (stats) { - is AddStatsItem -> 0L + is AddStatsItem -> AddStatsItem::class.hashCode().toLong() is StatsItem -> { if (stats is LocalIncidenceStats) { - stats.selectedDistrict?.district?.districtId?.toLong() ?: 0L + stats.selectedDistrict.district.districtId.toLong() } else { stats.cardType.id.toLong() } 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 7b0a197c13e..264d938096e 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 @@ -214,7 +214,7 @@ class HomeFragmentViewModel @AssistedInject constructor( onRemoveListener = { localStatisticsConfigStorage.activeDistricts.update { districts -> districts.filter { selected -> - selected.district.districtId != it.selectedDistrict?.district?.districtId + selected.district.districtId != it.selectedDistrict.district.districtId }.toSet() } } From 1495c8e76866f18e74890895ce00898650390122 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Tue, 13 Jul 2021 10:49:48 +0200 Subject: [PATCH 13/17] Fixed card recycling Added distinct condition to the provided stats as the same package can contain multiple districts --- .../local/source/LocalStatisticsProvider.kt | 3 ++- .../ui/homecards/cards/LocalIncidenceCard.kt | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt index c75c47730c7..45b60533be2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsProvider.kt @@ -50,7 +50,8 @@ class LocalStatisticsProvider @Inject constructor( groupedStats.copy( items = groupedStats.items - .sortedBy { selected -> selected.selectedDistrict.addedAt } + .distinctBy { it.selectedDistrict.district.districtId } + .sortedBy { it.selectedDistrict.addedAt } .reversed() ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt index bd980f005e1..5b53b1e6b18 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -32,19 +32,21 @@ class LocalIncidenceCard(parent: ViewGroup) : override val onBindData: HomeStatisticsCardsLocalIncidenceLayoutBinding.( item: StatisticsCardItem, payloads: List - ) -> Unit = { item, _ -> - with(item.stats as LocalIncidenceStats) { + ) -> Unit = { item, payloads -> + val curItem = payloads.filterIsInstance().singleOrNull() ?: item + + with(curItem.stats as LocalIncidenceStats) { overflowMenuButton.setupMenu(R.menu.menu_statistics_local_incidence) { when (it.itemId) { - R.id.menu_information -> item.onClickListener(item.stats).let { true } - R.id.menu_remove_item -> item.onRemoveListener(item.stats).let { true } + R.id.menu_information -> curItem.onClickListener(curItem.stats).let { true } + R.id.menu_remove_item -> curItem.onRemoveListener(curItem.stats).let { true } else -> false } } incidenceContainer.contentDescription = - buildAccessibilityStringForLocalIncidenceCard(item.stats, sevenDayIncidence) + buildAccessibilityStringForLocalIncidenceCard(curItem.stats, sevenDayIncidence) locationLabel.text = selectedDistrict.district.districtName From c0a90fec72b4bec5f38609849f101804497279a3 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Tue, 13 Jul 2021 12:14:01 +0200 Subject: [PATCH 14/17] Refactored the Statistics Cards into different global and local subtypes --- .../rki/coronawarnapp/statistics/StatsItem.kt | 33 ++++++++---- .../statistics/source/StatisticsParser.kt | 29 +++++----- .../ui/homecards/StatisticsCardAdapter.kt | 54 +++++++++++++------ .../ui/homecards/StatisticsHomeCard.kt | 16 ++++-- .../statistics/ui/homecards/cards/AddCard.kt | 7 ++- .../cards/AppliedVaccinationRatesCard.kt | 8 +-- .../ui/homecards/cards/IncidenceCard.kt | 8 +-- .../ui/homecards/cards/InfectionsCard.kt | 10 ++-- .../ui/homecards/cards/KeySubmissionsCard.kt | 8 +-- .../ui/homecards/cards/LocalIncidenceCard.kt | 10 ++-- .../cards/PersonsVaccinatedCompletelyCard.kt | 8 +-- .../cards/PersonsVaccinatedOnceCard.kt | 8 +-- .../ui/homecards/cards/SevenDayRValueCard.kt | 9 ++-- .../ui/homecards/cards/StatisticsCardItem.kt | 38 +++++++------ .../ui/main/home/HomeFragmentViewModel.kt | 15 ++++-- .../util/formatter/FormatterStatistics.kt | 21 ++++++-- .../statistics/StatisticsDataTest.kt | 8 +-- 17 files changed, 182 insertions(+), 108 deletions(-) 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 5b54fc37b38..8d45d51ac5a 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 @@ -15,7 +15,8 @@ data class StatisticsData( items.map { when (it) { is AddStatsItem -> "AddCard(${it.isEnabled})" - is StatsItem -> it.cardType.name + " " + it.updatedAt + is GlobalStatsItem -> it.cardType.name + " " + it.updatedAt + is LocalStatsItem -> it.cardType.name + " " + it.updatedAt } } })" @@ -40,7 +41,7 @@ sealed class GenericStatsItem data class AddStatsItem(val isEnabled: Boolean) : GenericStatsItem() -sealed class StatsItem(val cardType: Type) : GenericStatsItem() { +sealed class GlobalStatsItem(val cardType: Type) : GenericStatsItem() { abstract val updatedAt: Instant abstract val keyFigures: List @@ -51,7 +52,17 @@ sealed class StatsItem(val cardType: Type) : GenericStatsItem() { SEVEN_DAY_RVALUE(4), PERSONS_VACCINATED_ONCE(5), PERSONS_VACCINATED_COMPLETELY(6), - APPLIED_VACCINATION_RATES(7), + APPLIED_VACCINATION_RATES(7) + } + + abstract fun requireValidity() +} + +sealed class LocalStatsItem(val cardType: Type) : GenericStatsItem() { + abstract val updatedAt: Instant + abstract val keyFigures: List + + enum class Type(val id: Int) { LOCAL_INCIDENCE(8) } @@ -61,7 +72,7 @@ sealed class StatsItem(val cardType: Type) : GenericStatsItem() { data class InfectionStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.INFECTION) { +) : GlobalStatsItem(cardType = Type.INFECTION) { val newInfections: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -89,7 +100,7 @@ data class InfectionStats( data class IncidenceStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.INCIDENCE) { +) : GlobalStatsItem(cardType = Type.INCIDENCE) { val sevenDayIncidence: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -106,7 +117,7 @@ data class LocalIncidenceStats( override val updatedAt: Instant, override val keyFigures: List, val selectedDistrict: SelectedDistrict, -) : StatsItem(cardType = Type.LOCAL_INCIDENCE) { +) : LocalStatsItem(cardType = Type.LOCAL_INCIDENCE) { val sevenDayIncidence: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -122,7 +133,7 @@ data class LocalIncidenceStats( data class KeySubmissionsStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.KEYSUBMISSION) { +) : GlobalStatsItem(cardType = Type.KEYSUBMISSION) { val keySubmissions: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -150,7 +161,7 @@ data class KeySubmissionsStats( data class SevenDayRValue( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.SEVEN_DAY_RVALUE) { +) : GlobalStatsItem(cardType = Type.SEVEN_DAY_RVALUE) { val reproductionNumber: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -166,7 +177,7 @@ data class SevenDayRValue( data class PersonsVaccinatedOnceStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.PERSONS_VACCINATED_ONCE) { +) : GlobalStatsItem(cardType = Type.PERSONS_VACCINATED_ONCE) { val firstDose: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -188,7 +199,7 @@ data class PersonsVaccinatedOnceStats( data class PersonsVaccinatedCompletelyStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.PERSONS_VACCINATED_COMPLETELY) { +) : GlobalStatsItem(cardType = Type.PERSONS_VACCINATED_COMPLETELY) { val allDoses: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } @@ -210,7 +221,7 @@ data class PersonsVaccinatedCompletelyStats( data class AppliedVaccinationRatesStats( override val updatedAt: Instant, override val keyFigures: List -) : StatsItem(cardType = Type.APPLIED_VACCINATION_RATES) { +) : GlobalStatsItem(cardType = Type.APPLIED_VACCINATION_RATES) { val administeredDoses: KeyFigure get() = keyFigures.single { it.rank == KeyFigure.Rank.PRIMARY } 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 index 4287b61694b..4896c8f39d6 100644 --- 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 @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.statistics.source import dagger.Reusable import de.rki.coronawarnapp.server.protocols.internal.stats.StatisticsOuterClass import de.rki.coronawarnapp.statistics.AppliedVaccinationRatesStats +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.KeySubmissionsStats @@ -10,7 +11,6 @@ import de.rki.coronawarnapp.statistics.PersonsVaccinatedCompletelyStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedOnceStats 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 @@ -29,30 +29,33 @@ class StatisticsParser @Inject constructor() { ) } - val mappedItems: Set = parsed.keyFigureCardsList.mapNotNull { rawCard -> + val mappedItems: Set = 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) - StatsItem.Type.PERSONS_VACCINATED_ONCE -> PersonsVaccinatedOnceStats( + when (GlobalStatsItem.Type.values().singleOrNull { it.id == rawCard.header.cardId }) { + GlobalStatsItem.Type.INFECTION -> InfectionStats(updatedAt = updatedAt, keyFigures = keyFigures) + GlobalStatsItem.Type.INCIDENCE -> IncidenceStats(updatedAt = updatedAt, keyFigures = keyFigures) + GlobalStatsItem.Type.KEYSUBMISSION -> KeySubmissionsStats( updatedAt = updatedAt, keyFigures = keyFigures ) - StatsItem.Type.PERSONS_VACCINATED_COMPLETELY -> PersonsVaccinatedCompletelyStats( + GlobalStatsItem.Type.SEVEN_DAY_RVALUE -> SevenDayRValue( updatedAt = updatedAt, keyFigures = keyFigures ) - StatsItem.Type.APPLIED_VACCINATION_RATES -> AppliedVaccinationRatesStats( + GlobalStatsItem.Type.PERSONS_VACCINATED_ONCE -> PersonsVaccinatedOnceStats( + updatedAt = updatedAt, + keyFigures = keyFigures + ) + GlobalStatsItem.Type.PERSONS_VACCINATED_COMPLETELY -> PersonsVaccinatedCompletelyStats( + updatedAt = updatedAt, + keyFigures = keyFigures + ) + GlobalStatsItem.Type.APPLIED_VACCINATION_RATES -> AppliedVaccinationRatesStats( updatedAt = updatedAt, keyFigures = keyFigures ) - StatsItem.Type.LOCAL_INCIDENCE -> null.also { - Timber.tag(TAG).e("Statistics card of type local in global stats: %s", rawCard) - } null -> null.also { Timber.tag(TAG).e("Unknown statistics type: %s", rawCard) } }.also { Timber.tag(TAG).v("Parsed %s", it.toString().replace("\n", ", ")) 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 5b4a5fbd128..36b8008f975 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 @@ -3,22 +3,23 @@ package de.rki.coronawarnapp.statistics.ui.homecards import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding -import de.rki.coronawarnapp.statistics.AddStatsItem import de.rki.coronawarnapp.statistics.AppliedVaccinationRatesStats import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.KeySubmissionsStats -import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedCompletelyStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedOnceStats import de.rki.coronawarnapp.statistics.SevenDayRValue import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter.ItemVH import de.rki.coronawarnapp.statistics.ui.homecards.cards.AddCard +import de.rki.coronawarnapp.statistics.ui.homecards.cards.AddLocalStatisticsCardItem import de.rki.coronawarnapp.statistics.ui.homecards.cards.AppliedVaccinationRatesCard +import de.rki.coronawarnapp.statistics.ui.homecards.cards.GlobalStatisticsCardItem 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.LocalIncidenceCard +import de.rki.coronawarnapp.statistics.ui.homecards.cards.LocalStatisticsCardItem import de.rki.coronawarnapp.statistics.ui.homecards.cards.PersonsVaccinatedCompletelyCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.PersonsVaccinatedOnceCard import de.rki.coronawarnapp.statistics.ui.homecards.cards.SevenDayRValueCard @@ -41,19 +42,42 @@ class StatisticsCardAdapter : listOf( StableIdMod(data), DataBinderMod>(data), - TypedVHCreatorMod({ data[it].stats is InfectionStats }) { InfectionsCard(it) }, - TypedVHCreatorMod({ data[it].stats is IncidenceStats }) { IncidenceCard(it) }, - TypedVHCreatorMod({ data[it].stats is LocalIncidenceStats }) { LocalIncidenceCard(it) }, - TypedVHCreatorMod({ data[it].stats is KeySubmissionsStats }) { KeySubmissionsCard(it) }, - TypedVHCreatorMod({ data[it].stats is SevenDayRValue }) { SevenDayRValueCard(it) }, - TypedVHCreatorMod({ data[it].stats is PersonsVaccinatedOnceStats }) { PersonsVaccinatedOnceCard(it) }, - TypedVHCreatorMod({ data[it].stats is PersonsVaccinatedCompletelyStats }) { - PersonsVaccinatedCompletelyCard( - it - ) - }, - TypedVHCreatorMod({ data[it].stats is AppliedVaccinationRatesStats }) { AppliedVaccinationRatesCard(it) }, - TypedVHCreatorMod({ data[it].stats is AddStatsItem }) { AddCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is InfectionStats + }) { InfectionsCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is IncidenceStats + }) { IncidenceCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is KeySubmissionsStats + }) { KeySubmissionsCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is SevenDayRValue + }) { SevenDayRValueCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is PersonsVaccinatedOnceStats + }) { PersonsVaccinatedOnceCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is PersonsVaccinatedCompletelyStats + }) { PersonsVaccinatedCompletelyCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is GlobalStatisticsCardItem && item.stats is AppliedVaccinationRatesStats + }) { AppliedVaccinationRatesCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is AddLocalStatisticsCardItem + }) { AddCard(it) }, + TypedVHCreatorMod({ + val item = data[it] + item is LocalStatisticsCardItem + }) { LocalIncidenceCard(it) }, ).let { modules.addAll(it) } } 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 cb069830534..cb3df148cff 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 @@ -8,10 +8,14 @@ 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.AddStatsItem import de.rki.coronawarnapp.statistics.GenericStatsItem -import de.rki.coronawarnapp.statistics.LocalIncidenceStats +import de.rki.coronawarnapp.statistics.GlobalStatsItem +import de.rki.coronawarnapp.statistics.LocalStatsItem import de.rki.coronawarnapp.statistics.StatisticsData -import de.rki.coronawarnapp.statistics.ui.homecards.cards.StatisticsCardItem +import de.rki.coronawarnapp.statistics.ui.homecards.cards.AddLocalStatisticsCardItem +import de.rki.coronawarnapp.statistics.ui.homecards.cards.GlobalStatisticsCardItem +import de.rki.coronawarnapp.statistics.ui.homecards.cards.LocalStatisticsCardItem import de.rki.coronawarnapp.ui.main.home.HomeAdapter import de.rki.coronawarnapp.ui.main.home.items.HomeItem import de.rki.coronawarnapp.util.isPhone @@ -60,7 +64,11 @@ class StatisticsHomeCard( savedStateKey = "stats:${item.stableId}" item.data.items.map { - StatisticsCardItem(it, item.onClickListener, item.onRemoveListener) + when (it) { + is GlobalStatsItem -> GlobalStatisticsCardItem(it, item.onClickListener) + is AddStatsItem -> AddLocalStatisticsCardItem(it, item.onClickListener) + is LocalStatsItem -> LocalStatisticsCardItem(it, item.onClickListener, item.onRemoveListener) + } }.let { statisticsCardAdapter.update(it) } @@ -83,7 +91,7 @@ class StatisticsHomeCard( data class Item( val data: StatisticsData, val onClickListener: (GenericStatsItem) -> Unit, - val onRemoveListener: (LocalIncidenceStats) -> Unit = {}, + val onRemoveListener: (LocalStatsItem) -> Unit = {}, ) : HomeItem { override val stableId: Long = Item::class.java.name.hashCode().toLong() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt index 7ee4362d786..3b497fa28f8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt @@ -5,11 +5,10 @@ import androidx.core.content.ContextCompat import androidx.core.view.isGone import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsAddLayoutBinding -import de.rki.coronawarnapp.statistics.AddStatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter class AddCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_dashed_layout, parent ) { @@ -23,11 +22,11 @@ class AddCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsAddLayoutBinding.( - item: StatisticsCardItem, + item: AddLocalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> - with(item.stats as AddStatsItem) { + with(item.stats) { if (isEnabled) { warningText.isGone = true plusImage.clearColorFilter() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AppliedVaccinationRatesCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AppliedVaccinationRatesCard.kt index 084e91236cc..2f01779f558 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AppliedVaccinationRatesCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AppliedVaccinationRatesCard.kt @@ -5,7 +5,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsAppliedVaccinationRatesLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass import de.rki.coronawarnapp.statistics.AppliedVaccinationRatesStats -import de.rki.coronawarnapp.statistics.StatsItem +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends @@ -14,7 +14,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class AppliedVaccinationRatesCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -28,7 +28,7 @@ class AppliedVaccinationRatesCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsAppliedVaccinationRatesLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -82,7 +82,7 @@ class AppliedVaccinationRatesCard(parent: ViewGroup) : } private fun buildAccessibilityStringForPersonsVaccinatedOnceCard( - item: StatsItem, + item: GlobalStatsItem, administeredDoses: KeyFigureCardOuterClass.KeyFigure, sevenDayAverage: KeyFigureCardOuterClass.KeyFigure, total: KeyFigureCardOuterClass.KeyFigure 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 73d7139ad30..0cc760cddaa 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 @@ -4,8 +4,8 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsIncidenceLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.IncidenceStats -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends @@ -15,7 +15,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class IncidenceCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -29,7 +29,7 @@ class IncidenceCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsIncidenceLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -65,7 +65,7 @@ class IncidenceCard(parent: ViewGroup) : } private fun buildAccessibilityStringForIncidenceCard( - item: StatsItem, + item: GlobalStatsItem, sevenDayIncidence: KeyFigureCardOuterClass.KeyFigure ): StringBuilder { 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 c0ded5b0de7..e292d343d4d 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 @@ -4,17 +4,17 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsInfectionsLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.InfectionStats -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue -import de.rki.coronawarnapp.util.formatter.getPrimaryLabel import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithLineBreak import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace +import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class InfectionsCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -28,7 +28,7 @@ class InfectionsCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsInfectionsLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -72,7 +72,7 @@ class InfectionsCard(parent: ViewGroup) : } private fun buildAccessibilityStringForInfectionsCard( - item: StatsItem, + item: GlobalStatsItem, newInfections: KeyFigureCardOuterClass.KeyFigure, sevenDayAverage: KeyFigureCardOuterClass.KeyFigure, total: KeyFigureCardOuterClass.KeyFigure 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 7b047580673..86dffaa4df1 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 @@ -4,8 +4,8 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsKeysubmissionsLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.KeySubmissionsStats -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends @@ -14,7 +14,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class KeySubmissionsCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -28,7 +28,7 @@ class KeySubmissionsCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsKeysubmissionsLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -72,7 +72,7 @@ class KeySubmissionsCard(parent: ViewGroup) : } private fun buildAccessibilityStringForKeySubmissionsCard( - item: StatsItem, + item: GlobalStatsItem, keySubmissions: KeyFigureCardOuterClass.KeyFigure, sevenDayAverage: KeyFigureCardOuterClass.KeyFigure, total: KeyFigureCardOuterClass.KeyFigure diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt index 5b53b1e6b18..752c05d2230 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/LocalIncidenceCard.kt @@ -5,7 +5,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsLocalIncidenceLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass import de.rki.coronawarnapp.statistics.LocalIncidenceStats -import de.rki.coronawarnapp.statistics.StatsItem +import de.rki.coronawarnapp.statistics.LocalStatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends @@ -16,7 +16,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class LocalIncidenceCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -30,10 +30,10 @@ class LocalIncidenceCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsLocalIncidenceLayoutBinding.( - item: StatisticsCardItem, + item: LocalStatisticsCardItem, payloads: List ) -> Unit = { item, payloads -> - val curItem = payloads.filterIsInstance().singleOrNull() ?: item + val curItem = payloads.filterIsInstance().singleOrNull() ?: item with(curItem.stats as LocalIncidenceStats) { @@ -73,7 +73,7 @@ class LocalIncidenceCard(parent: ViewGroup) : } private fun buildAccessibilityStringForLocalIncidenceCard( - item: StatsItem, + item: LocalStatsItem, sevenDayIncidence: KeyFigureCardOuterClass.KeyFigure ): StringBuilder { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedCompletelyCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedCompletelyCard.kt index 03f6db77bca..0a3303815f9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedCompletelyCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedCompletelyCard.kt @@ -4,8 +4,8 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsVaccinatedCompletelyLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.PersonsVaccinatedCompletelyStats -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatPercentageValue import de.rki.coronawarnapp.statistics.util.formatStatisticalValue @@ -14,7 +14,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class PersonsVaccinatedCompletelyCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -27,7 +27,7 @@ class PersonsVaccinatedCompletelyCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsVaccinatedCompletelyLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -55,7 +55,7 @@ class PersonsVaccinatedCompletelyCard(parent: ViewGroup) : } private fun buildAccessibilityStringForPersonsVaccinatedCompletelyCard( - item: StatsItem, + item: GlobalStatsItem, firstDose: KeyFigureCardOuterClass.KeyFigure, total: KeyFigureCardOuterClass.KeyFigure ): StringBuilder { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedOnceCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedOnceCard.kt index e055a5f7a44..6143a637d09 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedOnceCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/PersonsVaccinatedOnceCard.kt @@ -4,8 +4,8 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsVaccinatedOnceLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.PersonsVaccinatedOnceStats -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatPercentageValue import de.rki.coronawarnapp.statistics.util.formatStatisticalValue @@ -14,7 +14,7 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class PersonsVaccinatedOnceCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -28,7 +28,7 @@ class PersonsVaccinatedOnceCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsVaccinatedOnceLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -56,7 +56,7 @@ class PersonsVaccinatedOnceCard(parent: ViewGroup) : } private fun buildAccessibilityStringForPersonsVaccinatedOnceCard( - item: StatsItem, + item: GlobalStatsItem, firstDose: KeyFigureCardOuterClass.KeyFigure, total: KeyFigureCardOuterClass.KeyFigure ): StringBuilder { 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 index d712f52de59..71f14dc9b88 100644 --- 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 @@ -4,8 +4,8 @@ import android.view.ViewGroup import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.HomeStatisticsCardsSevendayrvalueLayoutBinding import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.SevenDayRValue -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsCardAdapter import de.rki.coronawarnapp.statistics.util.formatStatisticalValue import de.rki.coronawarnapp.statistics.util.getContentDescriptionForTrends @@ -15,7 +15,8 @@ import de.rki.coronawarnapp.util.StringBuilderExtension.appendWithTrailingSpace import de.rki.coronawarnapp.util.formatter.getPrimaryLabel class SevenDayRValueCard(parent: ViewGroup) : - StatisticsCardAdapter.ItemVH( + + StatisticsCardAdapter.ItemVH( R.layout.home_statistics_cards_basecard_layout, parent ) { @@ -29,7 +30,7 @@ class SevenDayRValueCard(parent: ViewGroup) : } override val onBindData: HomeStatisticsCardsSevendayrvalueLayoutBinding.( - item: StatisticsCardItem, + item: GlobalStatisticsCardItem, payloads: List ) -> Unit = { item, _ -> @@ -65,7 +66,7 @@ class SevenDayRValueCard(parent: ViewGroup) : } private fun buildAccessibilityStringForSevenDayRValueCard( - item: StatsItem, + item: GlobalStatsItem, reproductionNumber: KeyFigureCardOuterClass.KeyFigure ): StringBuilder { 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 31298f2da47..8ca9fd55fa6 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 @@ -1,25 +1,33 @@ package de.rki.coronawarnapp.statistics.ui.homecards.cards import de.rki.coronawarnapp.statistics.AddStatsItem -import de.rki.coronawarnapp.statistics.GenericStatsItem +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.LocalIncidenceStats -import de.rki.coronawarnapp.statistics.StatsItem +import de.rki.coronawarnapp.statistics.LocalStatsItem import de.rki.coronawarnapp.util.lists.HasStableId -data class StatisticsCardItem( - val stats: GenericStatsItem, - val onClickListener: (GenericStatsItem) -> Unit, - val onRemoveListener: (LocalIncidenceStats) -> Unit, -) : HasStableId { +sealed class StatisticsCardItem : HasStableId + +data class GlobalStatisticsCardItem( + val stats: GlobalStatsItem, + val onClickListener: (GlobalStatsItem) -> Unit, +) : StatisticsCardItem() { + override val stableId: Long = stats.cardType.id.toLong() +} +data class AddLocalStatisticsCardItem( + val stats: AddStatsItem, + val onClickListener: (AddStatsItem) -> Unit, +) : StatisticsCardItem() { + override val stableId: Long = AddStatsItem::class.hashCode().toLong() +} + +data class LocalStatisticsCardItem( + val stats: LocalStatsItem, + val onClickListener: (LocalIncidenceStats) -> Unit, + val onRemoveListener: (LocalIncidenceStats) -> Unit, +) : StatisticsCardItem() { override val stableId: Long = when (stats) { - is AddStatsItem -> AddStatsItem::class.hashCode().toLong() - is StatsItem -> { - if (stats is LocalIncidenceStats) { - stats.selectedDistrict.district.districtId.toLong() - } else { - stats.cardType.id.toLong() - } - } + is LocalIncidenceStats -> stats.selectedDistrict.district.districtId.toLong() } } 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 264d938096e..e9ae4e22ffa 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 @@ -24,6 +24,7 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.core.CovidCertificateSe import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.statistics.AddStatsItem +import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.local.source.LocalStatisticsProvider import de.rki.coronawarnapp.statistics.local.storage.LocalStatisticsConfigStorage import de.rki.coronawarnapp.statistics.source.StatisticsProvider @@ -211,11 +212,15 @@ class HomeFragmentViewModel @AssistedInject constructor( else -> events.postValue(HomeFragmentEvents.GoToStatisticsExplanation) } }, - onRemoveListener = { - localStatisticsConfigStorage.activeDistricts.update { districts -> - districts.filter { selected -> - selected.district.districtId != it.selectedDistrict.district.districtId - }.toSet() + onRemoveListener = { statsItem -> + when (statsItem) { + is LocalIncidenceStats -> { + localStatisticsConfigStorage.activeDistricts.update { districts -> + districts.filter { + it.district.districtId != statsItem.selectedDistrict.district.districtId + }.toSet() + } + } } } ) 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 index ae982fab552..e42556a686b 100644 --- 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 @@ -4,19 +4,20 @@ import android.content.Context import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.util.getLocale import de.rki.coronawarnapp.statistics.AppliedVaccinationRatesStats +import de.rki.coronawarnapp.statistics.GlobalStatsItem import de.rki.coronawarnapp.statistics.IncidenceStats import de.rki.coronawarnapp.statistics.InfectionStats import de.rki.coronawarnapp.statistics.KeySubmissionsStats import de.rki.coronawarnapp.statistics.LocalIncidenceStats +import de.rki.coronawarnapp.statistics.LocalStatsItem import de.rki.coronawarnapp.statistics.PersonsVaccinatedCompletelyStats import de.rki.coronawarnapp.statistics.PersonsVaccinatedOnceStats import de.rki.coronawarnapp.statistics.SevenDayRValue -import de.rki.coronawarnapp.statistics.StatsItem import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone import org.joda.time.LocalDate import org.joda.time.format.DateTimeFormat -fun StatsItem.getPrimaryLabel(context: Context): String { +fun GlobalStatsItem.getPrimaryLabel(context: Context): String { val today = LocalDate() val yesterday = today.minusDays(1) val updatedAtDate = LocalDate(updatedAt.toUserTimeZone()) @@ -31,7 +32,6 @@ fun StatsItem.getPrimaryLabel(context: Context): String { else -> dateTimeFormatter.print(updatedAtDate) } is IncidenceStats, - is LocalIncidenceStats, is PersonsVaccinatedOnceStats, is PersonsVaccinatedCompletelyStats -> when (updatedAtDate) { today -> context.getString(R.string.statistics_primary_value_until_today) @@ -45,3 +45,18 @@ fun StatsItem.getPrimaryLabel(context: Context): String { } } } + +fun LocalStatsItem.getPrimaryLabel(context: Context): String { + val today = LocalDate() + val yesterday = today.minusDays(1) + val updatedAtDate = LocalDate(updatedAt.toUserTimeZone()) + val dateTimeFormatter = DateTimeFormat.mediumDate().withLocale(context.getLocale()) + + return when (this) { + is LocalIncidenceStats -> when (updatedAtDate) { + 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(updatedAtDate)) + } + } +} 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 index 065349ad936..41818c57137 100644 --- 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 @@ -28,7 +28,7 @@ class StatisticsDataTest { ) ) stats.apply { - cardType shouldBe StatsItem.Type.INFECTION + cardType shouldBe GlobalStatsItem.Type.INFECTION cardType.id shouldBe 1 newInfections.value shouldBe 1.0 sevenDayAverage.value shouldBe 2.0 @@ -54,7 +54,7 @@ class StatisticsDataTest { ) ) stats.apply { - cardType shouldBe StatsItem.Type.INCIDENCE + cardType shouldBe GlobalStatsItem.Type.INCIDENCE cardType.id shouldBe 2 sevenDayIncidence.value shouldBe 1.0 } @@ -87,7 +87,7 @@ class StatisticsDataTest { ) stats.apply { - cardType shouldBe StatsItem.Type.KEYSUBMISSION + cardType shouldBe GlobalStatsItem.Type.KEYSUBMISSION cardType.id shouldBe 3 keySubmissions.value shouldBe 1.0 sevenDayAverage.value shouldBe 2.0 @@ -114,7 +114,7 @@ class StatisticsDataTest { ) stats.apply { - cardType shouldBe StatsItem.Type.SEVEN_DAY_RVALUE + cardType shouldBe GlobalStatsItem.Type.SEVEN_DAY_RVALUE cardType.id shouldBe 4 reproductionNumber.value shouldBe 1.0 } From 5567ca2bc8684e198e7d84a9e0bf2e97ab826f46 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Tue, 13 Jul 2021 12:25:15 +0200 Subject: [PATCH 15/17] NRW is now also allowed to procure their local statistics --- .../coronawarnapp/statistics/local/FederalStateToPackageId.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt index 2400dff98bc..741fe9f7e29 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/FederalStateToPackageId.kt @@ -10,7 +10,7 @@ enum class FederalStateToPackageId(val packageId: Int) { HE(7), MV(3), NI(4), - NRW(5), + NW(5), RP(7), SL(7), SN(6), From 95b0e243502013365048ae76f394fe4f452bb478 Mon Sep 17 00:00:00 2001 From: Kolya Opahle Date: Tue, 13 Jul 2021 13:44:06 +0200 Subject: [PATCH 16/17] small xml formatting changes Disabled the AddCard completely (no ripple) --- .../coronawarnapp/statistics/ui/homecards/cards/AddCard.kt | 5 +++-- .../layout/home_statistics_cards_local_incidence_layout.xml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt index 3b497fa28f8..9be4d1f77cb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/ui/homecards/cards/AddCard.kt @@ -27,15 +27,16 @@ class AddCard(parent: ViewGroup) : ) -> Unit = { item, _ -> with(item.stats) { + warningText.isGone = isEnabled + container.isEnabled = isEnabled + if (isEnabled) { - warningText.isGone = true plusImage.clearColorFilter() titleText.setTextColor(ContextCompat.getColor(context, R.color.colorStatisticsPrimaryValue)) container.setOnClickListener { item.onClickListener(item.stats) } } else { - warningText.isGone = false plusImage.setColorFilter(ContextCompat.getColor(context, R.color.colorStatisticsValueLabel)) titleText.setTextColor(ContextCompat.getColor(context, R.color.colorStatisticsValueLabel)) } diff --git a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml index 6f787e92853..9124bb4dabb 100644 --- a/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml +++ b/Corona-Warn-App/src/main/res/layout/home_statistics_cards_local_incidence_layout.xml @@ -5,7 +5,6 @@ android:id="@+id/incidence_container" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:ignore="MissingConstraints" tools:layout_height="wrap_content" tools:layout_width="@dimen/statistics_card_width"> @@ -80,6 +79,7 @@ android:id="@+id/overflow_menu_button" style="@style/CardOverFlowButton" app:layout_constraintEnd_toEndOf="parent" + android:contentDescription="@string/button_menu" app:layout_constraintTop_toTopOf="parent" /> Date: Tue, 13 Jul 2021 15:34:03 +0200 Subject: [PATCH 17/17] Local Stats trend semantic is now inferred by trend --- .../local/source/LocalStatisticsParser.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt index 40ae9ac2861..437ab1d4acf 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/local/source/LocalStatisticsParser.kt @@ -1,7 +1,7 @@ package de.rki.coronawarnapp.statistics.local.source import dagger.Reusable -import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass +import de.rki.coronawarnapp.server.protocols.internal.stats.KeyFigureCardOuterClass.KeyFigure import de.rki.coronawarnapp.server.protocols.internal.stats.LocalStatisticsOuterClass import de.rki.coronawarnapp.statistics.LocalIncidenceStats import de.rki.coronawarnapp.statistics.LocalStatisticsData @@ -59,13 +59,27 @@ class LocalStatisticsParser @Inject constructor( } } - private fun LocalStatisticsOuterClass.SevenDayIncidenceData.toKeyFigure(): KeyFigureCardOuterClass.KeyFigure = - KeyFigureCardOuterClass.KeyFigure.newBuilder() - .setRank(KeyFigureCardOuterClass.KeyFigure.Rank.PRIMARY) + private fun matchTrendToSemantic(trend: KeyFigure.Trend) = + when (trend) { + KeyFigure.Trend.INCREASING -> + KeyFigure.TrendSemantic.NEGATIVE + KeyFigure.Trend.UNSPECIFIED_TREND -> + KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC + KeyFigure.Trend.STABLE -> + KeyFigure.TrendSemantic.NEUTRAL + KeyFigure.Trend.DECREASING -> + KeyFigure.TrendSemantic.POSITIVE + KeyFigure.Trend.UNRECOGNIZED -> + KeyFigure.TrendSemantic.UNRECOGNIZED + } + + private fun LocalStatisticsOuterClass.SevenDayIncidenceData.toKeyFigure(): KeyFigure = + KeyFigure.newBuilder() + .setRank(KeyFigure.Rank.PRIMARY) .setValue(value) .setDecimals(0) .setTrend(trend) - .setTrendSemantic(KeyFigureCardOuterClass.KeyFigure.TrendSemantic.UNSPECIFIED_TREND_SEMANTIC) + .setTrendSemantic(matchTrendToSemantic(trend)) .build() companion object {