Skip to content
Snippets Groups Projects
Unverified Commit fc844e3a authored by Mohamed Metwalli's avatar Mohamed Metwalli Committed by GitHub
Browse files

Fix Presence tracing Crash (EXPOSUREAPP-7751) (#3404)


* Handle validation errors from deep-links

* Add unit tests

Co-authored-by: default avatarMatthias Urhahn <matthias.urhahn@sap.com>
parent c20fa348
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.ui.presencetracing.attendee.checkins ...@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.ui.presencetracing.attendee.checkins
import de.rki.coronawarnapp.presencetracing.checkins.CheckIn import de.rki.coronawarnapp.presencetracing.checkins.CheckIn
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.VerifiedTraceLocation import de.rki.coronawarnapp.presencetracing.checkins.qrcode.VerifiedTraceLocation
import de.rki.coronawarnapp.util.ui.LazyString
sealed class CheckInEvent { sealed class CheckInEvent {
...@@ -17,6 +18,8 @@ sealed class CheckInEvent { ...@@ -17,6 +18,8 @@ sealed class CheckInEvent {
data class ConfirmSwipeItem(val checkIn: CheckIn, val position: Int) : CheckInEvent() data class ConfirmSwipeItem(val checkIn: CheckIn, val position: Int) : CheckInEvent()
data class InvalidQrCode(val errorText: LazyString) : CheckInEvent()
object ShowInformation : CheckInEvent() object ShowInformation : CheckInEvent()
object OpenDeviceSettings : CheckInEvent() object OpenDeviceSettings : CheckInEvent()
......
...@@ -31,6 +31,7 @@ import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator ...@@ -31,6 +31,7 @@ import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator
import de.rki.coronawarnapp.util.lists.diffutil.update import de.rki.coronawarnapp.util.lists.diffutil.update
import de.rki.coronawarnapp.util.onScroll import de.rki.coronawarnapp.util.onScroll
import de.rki.coronawarnapp.util.tryHumanReadableError import de.rki.coronawarnapp.util.tryHumanReadableError
import de.rki.coronawarnapp.util.ui.LazyString
import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.doNavigate
import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.observe2
import de.rki.coronawarnapp.util.ui.viewBinding import de.rki.coronawarnapp.util.ui.viewBinding
...@@ -66,14 +67,8 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag ...@@ -66,14 +67,8 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag
bindRecycler() bindRecycler()
bindFAB() bindFAB()
viewModel.checkins.observe2(this) { items -> viewModel.checkins.observe2(this) { items -> updateViews(items) }
updateViews(items) viewModel.events.observe2(this) { it?.let { onNavigationEvent(it) } }
}
viewModel.events.observe2(this) {
onNavigationEvent(it)
}
viewModel.errorEvent.observe2(this) { viewModel.errorEvent.observe2(this) {
val errorForHumans = it.tryHumanReadableError(requireContext()) val errorForHumans = it.tryHumanReadableError(requireContext())
Toast.makeText(requireContext(), errorForHumans.description, Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), errorForHumans.description, Toast.LENGTH_LONG).show()
...@@ -85,7 +80,7 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag ...@@ -85,7 +80,7 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag
viewModel.checkCameraSettings() viewModel.checkCameraSettings()
} }
private fun onNavigationEvent(event: CheckInEvent?) { private fun onNavigationEvent(event: CheckInEvent) {
when (event) { when (event) {
is CheckInEvent.ConfirmCheckIn -> { is CheckInEvent.ConfirmCheckIn -> {
setupAxisTransition() setupAxisTransition()
...@@ -127,9 +122,21 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag ...@@ -127,9 +122,21 @@ class CheckInsFragment : Fragment(R.layout.trace_location_attendee_checkins_frag
doNavigate(CheckInsFragmentDirections.actionCheckInsFragmentToCheckInOnboardingFragment(false)) doNavigate(CheckInsFragmentDirections.actionCheckInsFragmentToCheckInOnboardingFragment(false))
} }
is CheckInEvent.OpenDeviceSettings -> openDeviceSettings() is CheckInEvent.OpenDeviceSettings -> openDeviceSettings()
is CheckInEvent.InvalidQrCode -> showInvalidQrCodeInformation(event.errorText)
} }
} }
private fun showInvalidQrCodeInformation(lazyErrorText: LazyString) {
val errorText = lazyErrorText.get(requireContext())
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.trace_location_attendee_invalid_qr_code_dialog_title)
.setMessage(getString(R.string.trace_location_attendee_invalid_qr_code_dialog_message, errorText))
.setPositiveButton(R.string.trace_location_attendee_invalid_qr_code_dialog_positive_button) { _, _ ->
// NO-OP
}
.show()
}
private fun updateViews(items: List<CheckInsItem>) { private fun updateViews(items: List<CheckInsItem>) {
checkInsAdapter.update(items) checkInsAdapter.update(items)
binding.apply { binding.apply {
......
...@@ -20,6 +20,8 @@ import de.rki.coronawarnapp.util.coroutine.AppScope ...@@ -20,6 +20,8 @@ import de.rki.coronawarnapp.util.coroutine.AppScope
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.flow.intervalFlow import de.rki.coronawarnapp.util.flow.intervalFlow
import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import de.rki.coronawarnapp.util.ui.toLazyString
import de.rki.coronawarnapp.util.ui.toResolvingString
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
...@@ -140,15 +142,24 @@ class CheckInsViewModel @AssistedInject constructor( ...@@ -140,15 +142,24 @@ class CheckInsViewModel @AssistedInject constructor(
} }
private fun verifyUri(uri: String) = launch { private fun verifyUri(uri: String) = launch {
Timber.i("uri: $uri") try {
val qrCodePayload = qrCodeUriParser.getQrCodePayload(uri) Timber.i("uri: $uri")
when (val verifyResult = traceLocationVerifier.verifyTraceLocation(qrCodePayload)) { val qrCodePayload = qrCodeUriParser.getQrCodePayload(uri)
is TraceLocationVerifier.VerificationResult.Valid -> events.postValue( when (val verifyResult = traceLocationVerifier.verifyTraceLocation(qrCodePayload)) {
if (cleanHistory) is TraceLocationVerifier.VerificationResult.Valid -> events.postValue(
CheckInEvent.ConfirmCheckInWithoutHistory(verifyResult.verifiedTraceLocation) if (cleanHistory)
else CheckInEvent.ConfirmCheckInWithoutHistory(verifyResult.verifiedTraceLocation)
CheckInEvent.ConfirmCheckIn(verifyResult.verifiedTraceLocation) else
) CheckInEvent.ConfirmCheckIn(verifyResult.verifiedTraceLocation)
)
is TraceLocationVerifier.VerificationResult.Invalid -> events.postValue(
CheckInEvent.InvalidQrCode(verifyResult.errorTextRes.toResolvingString())
)
}
} catch (e: Exception) {
Timber.d(e, "TraceLocation verification failed")
val msg = e.message ?: "QR-Code was invalid"
events.postValue(CheckInEvent.InvalidQrCode(msg.toLazyString()))
} }
} }
......
...@@ -6,11 +6,14 @@ import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository ...@@ -6,11 +6,14 @@ import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QRCodeUriParser import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QRCodeUriParser
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocationVerifier import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocationVerifier
import de.rki.coronawarnapp.presencetracing.checkins.checkout.CheckOutHandler import de.rki.coronawarnapp.presencetracing.checkins.checkout.CheckOutHandler
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.InvalidQrCodeDataException
import de.rki.coronawarnapp.presencetracing.checkins.qrcode.InvalidQrCodeUriException
import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass
import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.ActiveCheckInVH import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.ActiveCheckInVH
import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.CameraPermissionVH import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.CameraPermissionVH
import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.PastCheckInVH import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.items.PastCheckInVH
import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.permission.CameraPermissionProvider import de.rki.coronawarnapp.ui.presencetracing.attendee.checkins.permission.CameraPermissionProvider
import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf import io.kotest.matchers.types.shouldBeInstanceOf
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
...@@ -180,6 +183,32 @@ class CheckInsViewModelTest : BaseTest() { ...@@ -180,6 +183,32 @@ class CheckInsViewModelTest : BaseTest() {
} }
} }
@Test
fun `Handle uri InvalidQrCodeUriException`() = runBlockingTest {
every { savedState.get<String>("deeplink.last") } returns null
coEvery { qrCodeUriParser.getQrCodePayload(any()) } throws InvalidQrCodeUriException("Invalid")
val url = "https://e.coronawarn.app?v=1#place_holder"
shouldNotThrow<InvalidQrCodeUriException> {
createInstance(deepLink = url, scope = this).apply {
events.getOrAwaitValue().shouldBeInstanceOf<CheckInEvent.InvalidQrCode>()
}
}
}
@Test
fun `Handle uri InvalidQrCodeDataException`() = runBlockingTest {
every { savedState.get<String>("deeplink.last") } returns null
coEvery { qrCodeUriParser.getQrCodePayload(any()) } throws InvalidQrCodeDataException("Invalid")
val url = "https://e.coronawarn.app?v=1#place_holder"
shouldNotThrow<InvalidQrCodeDataException> {
createInstance(deepLink = url, scope = this).apply {
events.getOrAwaitValue().shouldBeInstanceOf<CheckInEvent.InvalidQrCode>()
}
}
}
private fun createInstance(deepLink: String?, scope: CoroutineScope) = private fun createInstance(deepLink: String?, scope: CoroutineScope) =
CheckInsViewModel( CheckInsViewModel(
savedState = savedState, savedState = savedState,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment