Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Commit

Permalink
Ramp Down Notice (EXPOSUREAPP-14422) (#5802)
Browse files Browse the repository at this point in the history
* Ramp Down Notice

* Update comment

* Rename

* Tests

* Update RampDownDataProviderTest.kt

* Rampdown notice UI (EXPOSUREAPP-14571) (#5803)

* add RampDownNoticeCard

* add RampdownNoticeFragment

* finish rampdown notice

* remove StatusTabNotice

* change naming

* change naming

* update navigation add night icon

* add screenshot test

* Tweaking

* Keep instance for subsequent  cals

Co-authored-by: Mohamed <[email protected]>

* remove screenshot test

* Test

Co-authored-by: Nikolaus Schauersberger <[email protected]>
  • Loading branch information
mtwalli and schauersbergern authored Jan 18, 2023
1 parent 02a414d commit 167ce2b
Show file tree
Hide file tree
Showing 25 changed files with 857 additions and 12 deletions.
1 change: 1 addition & 0 deletions Corona-Warn-App/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,4 @@
-keep class de.rki.coronawarnapp.presencetracing.warning.download.server.** { *; }
-keep class de.rki.coronawarnapp.srs.core.model.** { *; }
-keep class de.rki.coronawarnapp.submission.** { *; }
-keep class de.rki.coronawarnapp.ccl.rampdown.model.** { *; }
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.rampdown.ui.RampDownNoticeCard
import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard
import de.rki.coronawarnapp.submission.ui.homecards.PcrTestPositiveCard
import de.rki.coronawarnapp.submission.ui.homecards.PcrTestSubmissionDoneCard
Expand All @@ -22,6 +23,7 @@ import de.rki.coronawarnapp.tracing.ui.homecards.TracingStateItem
import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState
import de.rki.coronawarnapp.ui.main.home.items.FAQCard
import de.rki.coronawarnapp.ui.main.home.items.HomeItem
import de.rki.coronawarnapp.ui.main.home.rampdown.RampDownNotice
import de.rki.coronawarnapp.ui.statistics.Statistics
import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import io.mockk.MockKAnnotations
Expand Down Expand Up @@ -336,11 +338,16 @@ class HomeFragmentTest : BaseUITest() {
// LiveData item for fragments
private fun homeFragmentItemsLiveData(
tracingStateItem: TracingStateItem = HomeData.Tracing.LOW_RISK_ITEM_WITH_ENCOUNTERS,
submissionTestResultItems: List<TestResultItem> = listOf(HomeData.Submission.TEST_UNREGISTERED_ITEM)
submissionTestResultItems: List<TestResultItem> = listOf(HomeData.Submission.TEST_UNREGISTERED_ITEM),
showRampDownNotice: Boolean = false
): LiveData<List<HomeItem>> =
MutableLiveData(
mutableListOf<HomeItem>().apply {

if (showRampDownNotice) {
add(getRampDownNotice())
}

val hideTracingState = submissionTestResultItems.any {
it is PcrTestPositiveCard.Item ||
it is PcrTestSubmissionDoneCard.Item ||
Expand All @@ -359,6 +366,17 @@ class HomeFragmentTest : BaseUITest() {
add(FAQCard.Item {})
}
)

private fun getRampDownNotice() = RampDownNoticeCard.Item(
onClickAction = {},
rampDownNotice = RampDownNotice(
visible = true,
title = "Betriebsende",
subtitle = "Der Betrieb der Corona-Warn-App wird am xx.xx.xxxx eingestellt.",
description = "",
faqUrl = null
)
)
}

@Module
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package de.rki.coronawarnapp.ccl.rampdown.calculation

import androidx.annotation.VisibleForTesting
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import de.rki.coronawarnapp.ccl.configuration.model.CclInputParameters
import de.rki.coronawarnapp.ccl.configuration.model.getDefaultInputParameters
import de.rki.coronawarnapp.ccl.dccwalletinfo.calculation.CclJsonFunctions
import de.rki.coronawarnapp.ccl.dccwalletinfo.model.SystemTime
import de.rki.coronawarnapp.ccl.rampdown.model.RampDownInput
import de.rki.coronawarnapp.ccl.rampdown.model.RampDownOutput
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.serialization.BaseJackson
import kotlinx.coroutines.withContext
import java.time.ZonedDateTime
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RampDownCalculation @Inject constructor(
@BaseJackson private val mapper: ObjectMapper,
private val cclJsonFunctions: CclJsonFunctions,
private val dispatcherProvider: DispatcherProvider
) {

suspend fun getStatusTabNotice(
dateTime: ZonedDateTime = ZonedDateTime.now(),
): RampDownOutput = withContext(dispatcherProvider.IO) {
val output = cclJsonFunctions.evaluateFunction(
"getStatusTabNotice",
getDefaultInputParameters(dateTime).toInput(mapper)
)
mapper.treeToValue(output, RampDownOutput::class.java)
}
}

@VisibleForTesting
internal fun CclInputParameters.toInput(mapper: ObjectMapper) = mapper.valueToTree<JsonNode>(
RampDownInput(
os = os,
language = language,
now = SystemTime(
timestamp = now.timestamp,
localDate = now.localDate,
localDateTime = now.localDateTime,
localDateTimeMidnight = now.localDateTimeMidnight,
utcDate = now.utcDate,
utcDateTime = now.utcDateTime,
utcDateTimeMidnight = now.utcDateTimeMidnight,
)
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.rki.coronawarnapp.ccl.rampdown.model

import com.fasterxml.jackson.annotation.JsonProperty
import de.rki.coronawarnapp.ccl.dccwalletinfo.model.SystemTime

data class RampDownInput(
@JsonProperty("os")
val os: String,

@JsonProperty("language")
val language: String,

@JsonProperty("now")
val now: SystemTime,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.rki.coronawarnapp.ccl.rampdown.model

import com.fasterxml.jackson.annotation.JsonProperty
import de.rki.coronawarnapp.ccl.dccwalletinfo.model.CclText

data class RampDownOutput(
@JsonProperty("visible")
val visible: Boolean,

@JsonProperty("titleText")
val titleText: CclText?,

@JsonProperty("subtitleText")
val subtitleText: CclText?,

@JsonProperty("longText")
val longText: CclText?,

@JsonProperty("faqAnchor")
val faqAnchor: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package de.rki.coronawarnapp.rampdown.ui

import android.view.ViewGroup
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.databinding.HomeRampDownNoticeCardBinding
import de.rki.coronawarnapp.ui.main.home.HomeAdapter
import de.rki.coronawarnapp.ui.main.home.items.HomeItem
import de.rki.coronawarnapp.ui.main.home.rampdown.RampDownNotice
import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer

class RampDownNoticeCard(
parent: ViewGroup
) : HomeAdapter.HomeItemVH<RampDownNoticeCard.Item, HomeRampDownNoticeCardBinding>(
R.layout.home_card_container_layout,
parent
) {

override val viewBinding = lazy {
HomeRampDownNoticeCardBinding
.inflate(layoutInflater, itemView.findViewById(R.id.card_container), true)
}

override val onBindData: HomeRampDownNoticeCardBinding.(
item: Item,
payloads: List<Any>
) -> Unit = { item, payloads ->
val curItem = payloads.filterIsInstance<Item>().lastOrNull() ?: item
itemView.setOnClickListener { curItem.onClickAction(item) }
rampdownCardHeaderHeadline.text = curItem.rampDownNotice.title
rampdownCardContentBody.text = curItem.rampDownNotice.subtitle
}

data class Item(
val onClickAction: (Item) -> Unit,
val rampDownNotice: RampDownNotice
) : HomeItem, HasPayloadDiffer {
override val stableId = Item::class.hashCode().toLong()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.rki.coronawarnapp.rampdown.ui

import android.os.Bundle
import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.databinding.FragmentRampdownNoticeBinding
import de.rki.coronawarnapp.util.convertToHyperlink
import de.rki.coronawarnapp.util.ui.popBackStack
import de.rki.coronawarnapp.util.ui.viewBinding

class RampDownNoticeFragment : Fragment(R.layout.fragment_rampdown_notice) {

private val binding: FragmentRampdownNoticeBinding by viewBinding()
private val navArgs by navArgs<RampDownNoticeFragmentArgs>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val data = navArgs.rampDownNotice

binding.apply {
toolbar.title = data.title
toolbar.setNavigationOnClickListener { popBackStack() }
rampDownNoticeSubtitle.text = data.subtitle
rampDownNoticeLongtext.text = data.description
data.faqUrl?.let {
rampDownNoticeFaqAnchor.convertToHyperlink(it)
rampDownNoticeFaqAnchor.isVisible = true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.viewbinding.ViewBinding
import de.rki.coronawarnapp.familytest.ui.homecard.FamilyTestCard
import de.rki.coronawarnapp.rampdown.ui.RampDownNoticeCard
import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard
import de.rki.coronawarnapp.submission.ui.homecards.PcrTestErrorCard
import de.rki.coronawarnapp.submission.ui.homecards.PcrTestInvalidCard
Expand Down Expand Up @@ -78,6 +79,7 @@ class HomeAdapter :
TypedVHCreatorMod({ data[it] is RapidTestOutdatedCard.Item }) { RapidTestOutdatedCard(it) },
TypedVHCreatorMod({ data[it] is RegisterTestCard.Item }) { RegisterTestCard(it) },
TypedVHCreatorMod({ data[it] is StatisticsHomeCard.Item }) { StatisticsHomeCard(it) },
TypedVHCreatorMod({ data[it] is RampDownNoticeCard.Item }) { RampDownNoticeCard(it) },
SavedStateMod<HomeItemVH<HomeItem, ViewBinding>>() // For statistics card scroll position
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
}
openUrl(urlToOpen)
}

is HomeFragmentEvents.OpenRampDownNotice -> {
findNavController().navigate(
HomeFragmentDirections.actionMainFragmentToRampdownNoticeFragment(event.rampDownNotice)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.annotation.StringRes
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.coronatest.type.BaseCoronaTest
import de.rki.coronawarnapp.coronatest.type.TestIdentifier
import de.rki.coronawarnapp.ui.main.home.rampdown.RampDownNotice

sealed class HomeFragmentEvents {
data class ShowTracingExplanation(val maxEncounterAgeInDays: Long) : HomeFragmentEvents()
Expand All @@ -20,6 +21,7 @@ sealed class HomeFragmentEvents {
object GoToFamilyTests : HomeFragmentEvents()
data class GoToTestResultNegativeFragment(val identifier: TestIdentifier) : HomeFragmentEvents()
data class GoToTestResultKeysSharedFragment(val identifier: TestIdentifier) : HomeFragmentEvents()
data class OpenRampDownNotice(val rampDownNotice: RampDownNotice) : HomeFragmentEvents()

data class OpenIncompatibleUrl(val scanningSupported: Boolean) : HomeFragmentEvents() {
@get:StringRes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import de.rki.coronawarnapp.coronatest.latestOf
import de.rki.coronawarnapp.coronatest.testErrorsSingleEvent
import de.rki.coronawarnapp.coronatest.type.BaseCoronaTest.Type.PCR
import de.rki.coronawarnapp.coronatest.type.BaseCoronaTest.Type.RAPID_ANTIGEN
import de.rki.coronawarnapp.coronatest.type.PersonalCoronaTest
import de.rki.coronawarnapp.coronatest.type.TestIdentifier
import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest
import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR
Expand All @@ -23,6 +24,7 @@ import de.rki.coronawarnapp.familytest.core.model.FamilyCoronaTest
import de.rki.coronawarnapp.familytest.core.repository.FamilyTestRepository
import de.rki.coronawarnapp.familytest.ui.homecard.FamilyTestCard
import de.rki.coronawarnapp.main.CWASettings
import de.rki.coronawarnapp.rampdown.ui.RampDownNoticeCard
import de.rki.coronawarnapp.reyclebin.coronatest.RecycledCoronaTestsProvider
import de.rki.coronawarnapp.risk.RiskCardDisplayInfo
import de.rki.coronawarnapp.risk.RiskState
Expand Down Expand Up @@ -71,10 +73,12 @@ import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState
import de.rki.coronawarnapp.tracing.ui.statusbar.toHeaderState
import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowErrorResetDialog
import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowTracingExplanation
import de.rki.coronawarnapp.ui.main.home.rampdown.RampDownDataProvider
import de.rki.coronawarnapp.ui.main.home.items.CreateTraceLocationCard
import de.rki.coronawarnapp.ui.main.home.items.FAQCard
import de.rki.coronawarnapp.ui.main.home.items.HomeItem
import de.rki.coronawarnapp.ui.main.home.items.IncompatibleCard
import de.rki.coronawarnapp.ui.main.home.rampdown.RampDownNotice
import de.rki.coronawarnapp.ui.presencetracing.TraceLocationPreferences
import de.rki.coronawarnapp.util.TimeStamper
import de.rki.coronawarnapp.util.bluetooth.BluetoothSupport
Expand All @@ -100,6 +104,7 @@ class HomeFragmentViewModel @AssistedInject constructor(
tracingStateProviderFactory: TracingStateProvider.Factory,
coronaTestRepository: CoronaTestRepository,
combinedStatisticsProvider: CombinedStatisticsProvider,
rampDownDataProvider: RampDownDataProvider,
private val errorResetTool: EncryptionErrorResetTool,
private val tracingRepository: TracingRepository,
private val submissionRepository: SubmissionRepository,
Expand All @@ -113,7 +118,7 @@ class HomeFragmentViewModel @AssistedInject constructor(
private val localStatisticsConfigStorage: LocalStatisticsConfigStorage,
private val recycledTestProvider: RecycledCoronaTestsProvider,
private val riskCardDisplayInfo: RiskCardDisplayInfo,
private val familyTestRepository: FamilyTestRepository,
private val familyTestRepository: FamilyTestRepository
) : CWAViewModel(dispatcherProvider = dispatcherProvider) {

private val tracingStateProvider by lazy { tracingStateProviderFactory.create(isDetailsMode = false) }
Expand Down Expand Up @@ -182,21 +187,36 @@ class HomeFragmentViewModel @AssistedInject constructor(
tracingSettings.updateShowRiskLevelBadge(show = false)
}

private val testsData = combine(
coronaTestRepository.coronaTests,
familyTestRepository.familyTests,
appConfigProvider.currentConfig.map { it.coronaTestParameters }.distinctUntilChanged(),
) { coronaTests, familyTests, coronaTestParameters ->
TestsData(
coronaTests = coronaTests,
familyTests = familyTests,
coronaTestParameters = coronaTestParameters
)
}

val homeItems: LiveData<List<HomeItem>> = combine(
testsData,
tracingCardItems,
coronaTestRepository.coronaTests,
combinedStatisticsProvider.statistics,
appConfigProvider.currentConfig.map { it.coronaTestParameters }.distinctUntilChanged(),
familyTestRepository.familyTests
) { tracingItem, coronaTests, statsData, coronaTestParameters, familyTests ->
rampDownDataProvider.rampDownNotice,
) { testsData, tracingItem, statsData, rampDownNotice ->
mutableListOf<HomeItem>().apply {
Timber.d("rampDownNotice=%s", rampDownNotice)
if (rampDownNotice?.visible == true) {
addRampDownCard(rampDownNotice)
}
addRiskLevelCard(tracingItem)
addIncompatibleCard()
addTestCards(
coronaTests.latestOf<PCRCoronaTest>(),
coronaTests.latestOf<RACoronaTest>(),
coronaTestParameters,
familyTests
testsData.coronaTests.latestOf<PCRCoronaTest>(),
testsData.coronaTests.latestOf<RACoronaTest>(),
testsData.coronaTestParameters,
testsData.familyTests
)
addStatisticsCard(statsData)
addTraceLocationCard()
Expand Down Expand Up @@ -245,6 +265,15 @@ class HomeFragmentViewModel @AssistedInject constructor(
cwaSettings.updateWasTracingExplanationDialogShown(true)
}

private fun MutableList<HomeItem>.addRampDownCard(rampDownNotice: RampDownNotice) {
add(
RampDownNoticeCard.Item(
onClickAction = { events.postValue(HomeFragmentEvents.OpenRampDownNotice(rampDownNotice)) },
rampDownNotice = rampDownNotice
)
)
}

private fun MutableList<HomeItem>.addFaqCard() {
add(FAQCard.Item(onClickAction = { events.postValue(HomeFragmentEvents.OpenFAQUrl) }))
}
Expand Down Expand Up @@ -521,3 +550,9 @@ class HomeFragmentViewModel @AssistedInject constructor(
val TAG = tag<HomeFragmentViewModel>()
}
}

data class TestsData(
val coronaTests: Set<PersonalCoronaTest>,
val familyTests: Set<FamilyCoronaTest>,
val coronaTestParameters: CoronaTestConfig
)
Loading

0 comments on commit 167ce2b

Please sign in to comment.