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

Certificates onboarding screen (EXPOSUREAPP-7435) #3337

Merged
merged 10 commits into from
Jun 1, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment
import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragmentArgs
import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentViewModel
import io.mockk.MockKAnnotations
import io.mockk.impl.annotations.MockK
Expand All @@ -22,6 +23,10 @@ class VaccinationConsentFragmentTest : BaseUITest() {

@MockK lateinit var viewModel: VaccinationConsentViewModel

private val fragmentArgs = VaccinationConsentFragmentArgs(
showBottomNav = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why showBottomNav = false?
BottomNav is visible in Figma.

).toBundle()

@Before
fun setup() {
MockKAnnotations.init(this, relaxed = true)
Expand All @@ -40,13 +45,13 @@ class VaccinationConsentFragmentTest : BaseUITest() {

@Test
fun launch_fragment() {
launchFragment2<VaccinationConsentFragment>()
launchFragment2<VaccinationConsentFragment>(fragmentArgs)
}

@Screenshot
@Test
fun capture_screenshot() {
launchFragmentInContainer2<VaccinationConsentFragment>()
launchFragmentInContainer2<VaccinationConsentFragment>(fragmentArgs)
takeScreenshot<VaccinationConsentFragment>()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavController
import androidx.navigation.NavGraph
import com.google.android.material.bottomnavigation.BottomNavigationView
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
Expand All @@ -33,6 +32,7 @@ import de.rki.coronawarnapp.util.device.PowerManagement
import de.rki.coronawarnapp.util.di.AppInjector
import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper.Companion.getShortcutExtra
import de.rki.coronawarnapp.util.ui.findNavController
import de.rki.coronawarnapp.util.ui.findNestedGraph
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
import org.joda.time.LocalDate
Expand Down Expand Up @@ -98,6 +98,9 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector {
vm.isTraceLocationOnboardingDone.observe(this) { isOnboardingDone ->
startTraceLocationNestedGraphDestination(navController, isOnboardingDone)
}
vm.isVaccinationConsentGiven.observe(this) { isConsentGiven ->
startCertificatesNestedGraphDestination(navController, isConsentGiven)
}

vm.activeCheckIns.observe(this) { count ->
val targetId = R.id.trace_location_attendee_nav_graph
Expand Down Expand Up @@ -134,7 +137,7 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector {
private fun goToContactJournal() {
findViewById<BottomNavigationView>(R.id.main_bottom_navigation).selectedItemId =
R.id.contact_diary_nav_graph
val nestedGraph = navController.graph.findNode(R.id.contact_diary_nav_graph) as NavGraph
val nestedGraph = navController.findNestedGraph(R.id.contact_diary_nav_graph)

if (vm.isContactDiaryOnboardingDone.value == true) {
nestedGraph.startDestination = R.id.contactDiaryOverviewFragment
Expand All @@ -150,23 +153,29 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector {
}

private fun startContactDiaryNestedGraphDestination(navController: NavController, isOnboardingDone: Boolean) {
val nestedGraph = navController.graph.findNode(R.id.contact_diary_nav_graph) as NavGraph
nestedGraph.startDestination = if (isOnboardingDone) {
navController.findNestedGraph(R.id.contact_diary_nav_graph).startDestination = if (isOnboardingDone) {
R.id.contactDiaryOverviewFragment
} else {
R.id.contactDiaryOnboardingFragment
}
}

private fun startTraceLocationNestedGraphDestination(navController: NavController, isOnboardingDone: Boolean) {
val nestedGraph = navController.graph.findNode(R.id.trace_location_attendee_nav_graph) as NavGraph
nestedGraph.startDestination = if (isOnboardingDone) {
navController.findNestedGraph(R.id.trace_location_attendee_nav_graph).startDestination = if (isOnboardingDone) {
R.id.checkInsFragment
} else {
R.id.checkInOnboardingFragment
}
}

private fun startCertificatesNestedGraphDestination(navController: NavController, isConsentGiven: Boolean) {
navController.findNestedGraph(R.id.green_certificate_graph).startDestination = if (isConsentGiven) {
R.id.certificatesFragment
} else {
R.id.vaccinationConsentFragment
}
}

private fun navigateByIntentUri(intent: Intent?) {
val uriString = intent?.data?.toString() ?: return
Timber.i("Uri:$uriString")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import de.rki.coronawarnapp.util.device.BackgroundModeStatus
import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
import de.rki.coronawarnapp.vaccination.core.VaccinationSettings
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

Expand All @@ -29,6 +30,7 @@ class MainActivityViewModel @AssistedInject constructor(
private val backgroundNoise: BackgroundNoise,
private val onboardingSettings: OnboardingSettings,
private val traceLocationSettings: TraceLocationSettings,
private val vaccinationSettings: VaccinationSettings,
checkInRepository: CheckInRepository
) : CWAViewModel(
dispatcherProvider = dispatcherProvider
Expand All @@ -42,6 +44,8 @@ class MainActivityViewModel @AssistedInject constructor(
val isContactDiaryOnboardingDone: LiveData<Boolean> = mutableIsContactDiaryOnboardingDone
private val mutableIsTraceLocationOnboardingDone = MutableLiveData<Boolean>()
val isTraceLocationOnboardingDone: LiveData<Boolean> = mutableIsTraceLocationOnboardingDone
private val mutableIsVaccinationConsentGiven = MutableLiveData<Boolean>()
val isVaccinationConsentGiven: LiveData<Boolean> = mutableIsVaccinationConsentGiven

val activeCheckIns = checkInRepository.checkInsWithinRetention
.map { checkins -> checkins.filter { !it.completed }.size }
Expand Down Expand Up @@ -84,6 +88,7 @@ class MainActivityViewModel @AssistedInject constructor(
fun onBottomNavSelected() {
mutableIsContactDiaryOnboardingDone.value = contactDiarySettings.isOnboardingDone
mutableIsTraceLocationOnboardingDone.value = traceLocationSettings.isOnboardingDone
mutableIsVaccinationConsentGiven.value = vaccinationSettings.registrationAcknowledged
}

private suspend fun checkForEnergyOptimizedEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,4 @@ fun FragmentManager.findNavController(@IdRes id: Int): NavController {
* @param nestedGraphId
* @throws IllegalArgumentException if graph not found
*/
fun Fragment.findNestedGraph(@IdRes nestedGraphId: Int): NavGraph {
return findNavController().graph.findNode(nestedGraphId) as? NavGraph
?: throw IllegalArgumentException("Nested graph with id=$nestedGraphId not found")
}
fun Fragment.findNestedGraph(@IdRes nestedGraphId: Int) = findNavController().findNestedGraph(nestedGraphId)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.rki.coronawarnapp.util.ui

import androidx.annotation.IdRes
import androidx.navigation.NavController
import androidx.navigation.NavGraph

/**
* Finds nested graph [NavGraph] by Id.
* @param nestedGraphId
* @throws IllegalArgumentException if graph not found
*/
fun NavController.findNestedGraph(@IdRes nestedGraphId: Int): NavGraph {
return graph.findNode(nestedGraphId) as? NavGraph
?: throw IllegalArgumentException("Nested graph with id=$nestedGraphId not found")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package de.rki.coronawarnapp.vaccination.ui.consent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.databinding.VaccinationConsentFragmentBinding
import de.rki.coronawarnapp.util.ContextExtensions.getDrawableCompat
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.doNavigate
import de.rki.coronawarnapp.util.ui.observe2
Expand All @@ -18,11 +20,17 @@ class VaccinationConsentFragment : Fragment(R.layout.vaccination_consent_fragmen

@Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
private val viewModel: VaccinationConsentViewModel by cwaViewModels { viewModelFactory }

private val binding: VaccinationConsentFragmentBinding by viewBinding()
private val args by navArgs<VaccinationConsentFragmentArgs>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
with(binding) {
if (!args.showBottomNav) {
toolbar.apply {
navigationIcon = context.getDrawableCompat(R.drawable.ic_close)
navigationContentDescription = getString(R.string.accessibility_close)
}
}
toolbar.setNavigationOnClickListener { popBackStack() }
vaccinationConsentPrivacyInformation.setOnClickListener {
viewModel.onDataPrivacyClick()
Expand All @@ -35,10 +43,16 @@ class VaccinationConsentFragment : Fragment(R.layout.vaccination_consent_fragmen
viewModel.routeToScreen.observe2(this) {
when (it) {
VaccinationConsentNavigationEvent.NavigateToDataPrivacy -> {
doNavigate(VaccinationConsentFragmentDirections.vaccinationConsentFragmentToPrivacyFragment())
doNavigate(
VaccinationConsentFragmentDirections
.actionVaccinationConsentFragmentToPrivacyFragment()
)
}
VaccinationConsentNavigationEvent.NavigateToQrCodeScan -> {
doNavigate(VaccinationConsentFragmentDirections.vaccinationConsentFragmentToQrCodeFragment())
VaccinationConsentNavigationEvent.NavigateToCertificates -> {
doNavigate(
VaccinationConsentFragmentDirections
.actionVaccinationConsentFragmentToCertificatesFragment()
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package de.rki.coronawarnapp.vaccination.ui.consent

sealed class VaccinationConsentNavigationEvent {
object NavigateToDataPrivacy : VaccinationConsentNavigationEvent()
object NavigateToQrCodeScan : VaccinationConsentNavigationEvent()
object NavigateToCertificates : VaccinationConsentNavigationEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class VaccinationConsentViewModel @AssistedInject constructor(

fun onConsentClick() {
vaccinationSettings.registrationAcknowledged = true
routeToScreen.postValue(VaccinationConsentNavigationEvent.NavigateToQrCodeScan)
routeToScreen.postValue(VaccinationConsentNavigationEvent.NavigateToCertificates)
}

fun onDataPrivacyClick() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/CWAToolbar.Close.Transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<TextView
android:id="@+id/vaccination_consent_legal_text_card_title"
style="@style/subtitleBoldSixteen"
style="@style/headline5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,30 @@
android:name="de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesFragment"
android:label="fragment_certificates"
tools:layout="@layout/fragment_certificates" />

<fragment
android:id="@+id/vaccinationConsentFragment"
android:name="de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment"
android:label="vaccination_consent_fragment"
tools:layout="@layout/vaccination_consent_fragment" >
<argument
android:name="showBottomNav"
android:defaultValue="true"
app:argType="boolean" />
<action
android:id="@+id/action_vaccinationConsentFragment_to_certificatesFragment"
app:destination="@id/certificatesFragment"
app:popUpTo="@id/vaccinationConsentFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_vaccinationConsentFragment_to_privacyFragment"
app:destination="@id/privacyFragment" />
</fragment>

<fragment
android:id="@+id/privacyFragment"
android:name="de.rki.coronawarnapp.ui.information.InformationPrivacyFragment"
android:label="privacyFragment"
tools:layout="@layout/fragment_information_privacy" />

</navigation>
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/vaccination_nav_graph"
app:startDestination="@id/vaccinationConsentFragment">

<fragment
android:id="@+id/vaccinationConsentFragment"
android:name="de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment"
android:label="VaccinationConsentFragment"
tools:layout="@layout/vaccination_consent_fragment">
<action
android:id="@+id/vaccinationConsentFragment_to_privacyFragment"
app:destination="@id/privacyFragment" />
<action
android:id="@+id/vaccinationConsentFragment_to_QrCodeFragment"
app:destination="@id/vaccinationQrCodeScanFragment"
app:popUpTo="@id/vaccinationConsentFragment"
app:popUpToInclusive="true" />
</fragment>
app:startDestination="@id/vaccinationListFragment">

<fragment
android:id="@+id/vaccinationQrCodeScanFragment"
Expand Down Expand Up @@ -60,9 +45,4 @@
app:argType="string" />
</fragment>

<fragment
android:id="@+id/privacyFragment"
android:name="de.rki.coronawarnapp.ui.information.InformationPrivacyFragment"
android:label="privacyFragment"
tools:layout="@layout/fragment_information_privacy" />
</navigation>
8 changes: 4 additions & 4 deletions Corona-Warn-App/src/main/res/values-de/legal_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@
<!-- XHED: Title for privacy card -->
<string name="vaccination_privacy_card_title_text" translatable="false">"Datenschutz und Datensicherheit"</string>
<!-- XTXT: First bulletpoint title for privacy card -->
<string name="vaccination_privacy_card_first_bulletpoint_title_text" translatable="false">"Die Verwendung des digitalen Impfnachweises ist freiwillig. Der Nachweis des vollständigen Impfschutzes kann auch auf andere Weise erbracht werden (z.B. mit dem gelben Impfausweis)."</string>
<string name="vaccination_privacy_card_first_bulletpoint_title_text" translatable="false">"Die Verwendung der digitalen COVID-Zertifikate ist freiwillig. Die Nachweise des vollständigen Impfschutzes oder eines negativen Testergebnisses können auch auf andere Weise erbracht werden."</string>
<!-- XTXT: First bulletpoint title for privacy card -->
<string name="vaccination_privacy_card_second_bulletpoint_title_text" translatable="false">"Das Impfzertifikat enthält die Daten über Ihre Corona-Impfung. Zum Nachweis Ihres Impfschutzes in den gesetzlich vorgesehenen Fällen genügt das Vorzeigen des QR-Codes in der App. Stellen Sie die Impfzertifikate und QR-Codes niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten ausgelesen werden."</string>
<string name="vaccination_privacy_card_second_bulletpoint_title_text" translatable="false">"Die COVID-Zertifikate enthalten Ihre personenbezogenen Daten (zu Impfungen oder Ihrem Testergebnis). Zum Nachweis in den gesetzlich vorgesehenen Fällen genügt das Vorzeigen des QR-Codes des Zertifikats in der App. Stellen Sie Ihre Zertifikate und die QR-Codes niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten ausgelesen werden."</string>
<!-- XTXT: First bulletpoint title for privacy card -->
<string name="vaccination_privacy_card_third_bulletpoint_title_text" translatable="false">"Wenn Sie den QR-Code in der App vorzeigen und dieser mit der Prüf-App gescannt wird, können andere Personen nachvollziehen, ob Ihr Impfschutz vollständig ist. Bei der Prüfung werden in der offiziellen Prüf-App auch Ihr Name und Ihr Geburtsdatum angezeigt."</string>
<string name="vaccination_privacy_card_third_bulletpoint_title_text" translatable="false">"Wenn Sie den QR-Code eines Zertifikats in der App zur Prüfung vorzeigen, können andere Personen Ihr Testergebnis nachvollziehen oder ob Ihr Impfschutz vollständig ist. Bei der Prüfung werden in der offiziellen Prüf-App auch Ihr Name und Ihr Geburtsdatum angezeigt."</string>
<!-- XTXT: First bulletpoint title for privacy card -->
<string name="vaccination_privacy_card_fourth_bulletpoint_title_text" translatable="false">"Sie haben jederzeit die Möglichkeit, Impfzertifikate in der App wieder zu entfernen. Bis dahin bleiben die Impfzertifikate auf Ihrem Smartphone gespeichert."</string>
<string name="vaccination_privacy_card_fourth_bulletpoint_title_text" translatable="false">"Sie haben jederzeit die Möglichkeit, COVID-Zertifikate in der App wieder zu entfernen. Bis dahin bleiben die Zertifikate auf Ihrem Smartphone gespeichert."</string>

<!-- Green Certificate -->
<!-- XTXT: Request GC title for privacy card -->
Expand Down