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

[UI] Family test selection and consent screen (EXPOSUREAPP-12316, EXPOSUREAPP-12317) #4951

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
71fcf47
Setup UI flow from scanner
mtwalli Mar 17, 2022
4936c0a
Lint
mtwalli Mar 17, 2022
5ef6e3d
Normal and legal texts, layout and icons for submission test type sel…
AlexanderAlferov Mar 17, 2022
c7ee698
Merge remote-tracking branch 'origin/release/2.21.x' into feature/123…
AlexanderAlferov Mar 17, 2022
85e5268
Merge branch 'release/2.21.x' into feature/12316-12317-family-test-se…
mtwalli Mar 21, 2022
855fc5c
Basic logic
AlexanderAlferov Mar 21, 2022
d7d46b8
Merge branch 'feature/12316-12317-family-test-selection-and-consent' …
AlexanderAlferov Mar 21, 2022
de235f8
Strings aligned with iOS version
AlexanderAlferov Mar 22, 2022
3a3b727
Merge remote-tracking branch 'origin/release/2.21.x' into feature/123…
AlexanderAlferov Mar 22, 2022
a6aa1f8
Tests and fixes
AlexanderAlferov Mar 22, 2022
3dec0e9
Merge remote-tracking branch 'origin/release/2.21.x' into feature/123…
AlexanderAlferov Mar 22, 2022
ae80f9c
Legal strings fix
AlexanderAlferov Mar 22, 2022
a11326b
Merge branch 'release/2.21.x' into feature/12316-12317-family-test-se…
AlexanderAlferov Mar 22, 2022
38c137a
Merge branch 'release/2.21.x' into feature/12316-12317-family-test-se…
SamuraiKek Mar 23, 2022
ce8994f
Fix
AlexanderAlferov Mar 23, 2022
8b29c86
Merge branch 'feature/12316-12317-family-test-selection-and-consent' …
AlexanderAlferov Mar 23, 2022
0c8ea45
Merge branch 'release/2.21.x' into feature/12316-12317-family-test-se…
mtwalli Mar 23, 2022
a02406d
Lint
mtwalli Mar 23, 2022
c7ae7d4
Navigation fix
AlexanderAlferov Mar 24, 2022
1062088
Lint fix
AlexanderAlferov Mar 24, 2022
d05599d
Merge branch 'release/2.21.x' into feature/12316-12317-family-test-se…
mtwalli Mar 24, 2022
a5118a9
Fix
AlexanderAlferov Mar 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package de.rki.coronawarnapp.familytest.ui.consent

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
import io.mockk.MockKAnnotations
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import testhelpers.BaseUITest
import testhelpers.Screenshot
import testhelpers.TestDispatcherProvider
import testhelpers.launchFragmentInContainer2
import testhelpers.takeScreenshot

@RunWith(AndroidJUnit4::class)
class FamilyTestConsentFragmentTest : BaseUITest() {

private val request = CoronaTestQRCode.PCR(qrCodeGUID = "qrCodeGUID", rawQrCode = "rawQrCode")
private lateinit var viewModel: FamilyTestConsentViewModel

private val fragmentArgs = FamilyTestConsentFragmentArgs(
coronaTestQrCode = request
).toBundle()

@Before
fun setup() {
MockKAnnotations.init(this, relaxed = true)
viewModel = FamilyTestConsentViewModel(
TestDispatcherProvider(),
request
)
setupMockViewModel(
object : FamilyTestConsentViewModel.Factory {
override fun create(
coronaTestQRCode: CoronaTestQRCode
): FamilyTestConsentViewModel = viewModel
}
)
}

@After
fun teardown() {
clearAllViewModels()
}

@Test
@Screenshot
fun capture_fragment() {
launchFragmentInContainer2<FamilyTestConsentFragment>(fragmentArgs = fragmentArgs)
takeScreenshot<FamilyTestConsentFragment>("no_data")

onView(withId(R.id.name_input_edit))
.perform(click())
.perform(typeText("Lara"), closeSoftKeyboard())

onView(withId(R.id.scrollview)).perform(ViewActions.swipeDown())

takeScreenshot<FamilyTestConsentFragment>("with_data")
}
}

@Module
abstract class FamilyTestConsentFragmentTestModule {
@ContributesAndroidInjector
abstract fun familyTestConsentScreen(): FamilyTestConsentFragment
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.rki.coronawarnapp.familytest.ui.selection

import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import testhelpers.BaseUITest
import testhelpers.Screenshot
import testhelpers.launchFragmentInContainer2
import testhelpers.takeScreenshot

@RunWith(AndroidJUnit4::class)
class TestRegistrationSelectionFragmentTest : BaseUITest() {

@Test
@Screenshot
fun capture_fragment() {
launchFragmentInContainer2<TestRegistrationSelectionFragment>()
takeScreenshot<TestRegistrationSelectionFragment>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import de.rki.coronawarnapp.dccticketing.ui.validationresult.success.DccTicketin
import de.rki.coronawarnapp.dccticketing.ui.certificateselection.DccTicketingCertificateSelectionFragmentModule
import de.rki.coronawarnapp.dccticketing.ui.consent.two.DccTicketingConsentTwoFragmentModule
import de.rki.coronawarnapp.dccticketing.ui.consent.one.DccTicketingConsentOneFragmentTestModule
import de.rki.coronawarnapp.familytest.ui.consent.FamilyTestConsentFragmentTestModule
import de.rki.coronawarnapp.qrcode.ui.QrCodeScannerFragmentTestModule
import de.rki.coronawarnapp.reyclebin.ui.RecyclerBinOverviewFragmentTestModule
import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryDayFragmentTestModule
Expand Down Expand Up @@ -145,8 +146,11 @@ import de.rki.coronawarnapp.ui.vaccination.CovidCertificateInfoFragmentTestModul
DccTicketingConsentTwoFragmentModule::class,
DccTicketingConsentOneFragmentTestModule::class,

// Admission Scenarios
// --------- Admission Scenarios ---------
AdmissionScenariosFragmentTestModule::class,

// --------- Family Test Certificate ---------
FamilyTestConsentFragmentTestModule::class,
]
)
class FragmentTestModuleRegistrar
Original file line number Diff line number Diff line change
@@ -1,6 +1,93 @@
package de.rki.coronawarnapp.familytest.ui.consent

import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import de.rki.coronawarnapp.NavGraphDirections
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.contactdiary.util.hideKeyboard
import de.rki.coronawarnapp.databinding.FragmentFamilyTestConsentBinding
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.doNavigate
import de.rki.coronawarnapp.util.ui.observe2
import de.rki.coronawarnapp.util.ui.popBackStack
import de.rki.coronawarnapp.util.ui.viewBinding
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
import javax.inject.Inject

class FamilyTestConsentFragment : Fragment(R.layout.fragment_family_test_consent)
class FamilyTestConsentFragment : Fragment(R.layout.fragment_family_test_consent), AutoInject {

@Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
private val navArgs by navArgs<FamilyTestConsentFragmentArgs>()
private val viewModel: FamilyTestConsentViewModel by cwaViewModelsAssisted(
factoryProducer = { viewModelFactory },
constructorCall = { factory, _ ->
factory as FamilyTestConsentViewModel.Factory
factory.create(navArgs.coronaTestQrCode)
}
)
private val binding: FragmentFamilyTestConsentBinding by viewBinding()
private val navOptions = NavOptions.Builder().setPopUpTo(R.id.familyTestConsentFragment, true).build()

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

viewModel.routeToScreen.observe2(this) {
when (it) {
is FamilyTestConsentNavigationEvents.NavigateBack -> {
binding.root.hideKeyboard()
popBackStack()
}
is FamilyTestConsentNavigationEvents.NavigateToDataPrivacy -> doNavigate(
FamilyTestConsentFragmentDirections.actionFamilyTestConsentFragmentToInformationPrivacyFragment()
)
is FamilyTestConsentNavigationEvents.NavigateToCertificateRequest -> findNavController().navigate(
NavGraphDirections.actionRequestCovidCertificateFragment(
testRegistrationRequest = it.coronaTestQRCode,
coronaTestConsent = it.consentGiven,
allowTestReplacement = it.allowReplacement,
personName = it.personName
),
navOptions
)
else -> Unit
}
}

binding.apply {
nameInputEdit.doAfterTextChanged {
viewModel.nameChanged(it.toString())
}
nameInputEdit.setOnEditorActionListener { _, actionId, _ ->
return@setOnEditorActionListener when (actionId) {
EditorInfo.IME_ACTION_DONE -> {
if (viewModel.isValid.value == true) {
binding.consentButton.performClick()
}
false
}
else -> true
}
}
toolbar.setOnClickListener {
viewModel.onNavigateBack()
}
dataPrivacy.setOnClickListener {
viewModel.onDataPrivacyClick()
}
consentButton.setOnClickListener {
viewModel.onConsentButtonClick()
}
}

viewModel.isValid.observe2(this) {
binding.consentButton.isEnabled = it
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.rki.coronawarnapp.familytest.ui.consent

import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey

@Module
abstract class FamilyTestConsentModule {
@Binds
@IntoMap
@CWAViewModelKey(FamilyTestConsentViewModel::class)
abstract fun familyTestConsentFragment(
factory: FamilyTestConsentViewModel.Factory
): CWAViewModelFactory<out CWAViewModel>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.rki.coronawarnapp.familytest.ui.consent

import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode

sealed class FamilyTestConsentNavigationEvents {

data class NavigateToCertificateRequest(
val coronaTestQRCode: CoronaTestQRCode,
val consentGiven: Boolean,
val allowReplacement: Boolean,
val personName: String
) : FamilyTestConsentNavigationEvents()

object NavigateToDataPrivacy : FamilyTestConsentNavigationEvents()

object NavigateBack : FamilyTestConsentNavigationEvents()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.rki.coronawarnapp.familytest.ui.consent

import androidx.lifecycle.asLiveData
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

class FamilyTestConsentViewModel @AssistedInject constructor(
dispatcherProvider: DispatcherProvider,
@Assisted private val coronaTestQRCode: CoronaTestQRCode,
) : CWAViewModel(dispatcherProvider = dispatcherProvider) {

val routeToScreen = SingleLiveEvent<FamilyTestConsentNavigationEvents>()

private val personName = MutableStateFlow("")

val isValid = personName
.map { it.isNotEmpty() }
.asLiveData()

fun nameChanged(value: String) {
personName.value = value
}

fun onDataPrivacyClick() {
routeToScreen.postValue(FamilyTestConsentNavigationEvents.NavigateToDataPrivacy)
}

fun onNavigateBack() {
routeToScreen.postValue(FamilyTestConsentNavigationEvents.NavigateBack)
}

fun onConsentButtonClick() = launch {
FamilyTestConsentNavigationEvents.NavigateToCertificateRequest(
coronaTestQRCode = coronaTestQRCode,
consentGiven = true,
allowReplacement = true,
mtwalli marked this conversation as resolved.
Show resolved Hide resolved
personName = personName.first()
).run { routeToScreen.postValue(this) }
}

@AssistedFactory
interface Factory : CWAViewModelFactory<FamilyTestConsentViewModel> {
fun create(
coronaTestQRCode: CoronaTestQRCode
): FamilyTestConsentViewModel
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
package de.rki.coronawarnapp.familytest.ui.selection

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.FragmentTestRegistrationSelectionBinding
import de.rki.coronawarnapp.util.ui.doNavigate
import de.rki.coronawarnapp.util.ui.popBackStack
import de.rki.coronawarnapp.util.ui.viewBinding

class TestRegistrationSelectionFragment : Fragment(R.layout.fragment_test_registration_selection)
class TestRegistrationSelectionFragment : Fragment(R.layout.fragment_test_registration_selection) {

private val navArgs by navArgs<TestRegistrationSelectionFragmentArgs>()
private val binding: FragmentTestRegistrationSelectionBinding by viewBinding()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { popBackStack() }
binding.person.setOnClickListener {
doNavigate(
TestRegistrationSelectionFragmentDirections
.actionTestRegistrationSelectionFragmentToSubmissionConsentFragment(
mtwalli marked this conversation as resolved.
Show resolved Hide resolved
coronaTestQrCode = navArgs.coronaTestQrCode
mtwalli marked this conversation as resolved.
Show resolved Hide resolved
)
)
}
binding.family.setOnClickListener {
doNavigate(
TestRegistrationSelectionFragmentDirections
.actionTestRegistrationSelectionFragmentToFamilyTestConsentFragment(
coronaTestQrCode = navArgs.coronaTestQrCode
mtwalli marked this conversation as resolved.
Show resolved Hide resolved
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package de.rki.coronawarnapp.ui.submission.viewmodel

import dagger.Module
import dagger.android.ContributesAndroidInjector
import de.rki.coronawarnapp.familytest.ui.consent.FamilyTestConsentFragment
import de.rki.coronawarnapp.familytest.ui.consent.FamilyTestConsentModule
import de.rki.coronawarnapp.submission.ui.testresults.negative.RATResultNegativeFragment
import de.rki.coronawarnapp.submission.ui.testresults.negative.RATResultNegativeModule
import de.rki.coronawarnapp.ui.submission.fragment.SubmissionContactFragment
Expand Down Expand Up @@ -110,4 +112,7 @@ internal abstract class SubmissionFragmentModule {

@ContributesAndroidInjector(modules = [RequestCovidCertificateFragmentModule::class])
abstract fun requestCovidCertificateFragment(): RequestCovidCertificateFragment

@ContributesAndroidInjector(modules = [FamilyTestConsentModule::class])
abstract fun familyTestConsentFragment(): FamilyTestConsentFragment
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M0,15.6341a16,15.6341 0,1 0,32 0a16,15.6341 0,1 0,-32 0z"
android:fillColor="#434445"/>
<path
android:pathData="M26.6104,16.3141L24.2136,13.5735L24.5476,9.9488L21.0015,9.1433L19.145,6L15.8052,7.4342L12.4654,6L10.6089,9.1335L7.0628,9.9292L7.3968,13.5636L5,16.3141L7.3968,19.0547L7.0628,22.6891L10.6089,23.4946L12.4654,26.6281L15.8052,25.1842L19.145,26.6183L21.0015,23.4848L24.5476,22.6793L24.2136,19.0547L26.6104,16.3141ZM13.929,20.9505L10.1963,17.2079L11.6501,15.7542L13.929,18.0429L19.6754,12.2768L21.1292,13.7306L13.929,20.9505Z"
android:fillColor="#83D2F2"/>
</vector>
Loading