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

Cannot delete tests older than 21 days - DE: Tests die älter als 21 Tage sind, können nicht gelöscht werden (EXPOSUREAPP-2398) #1586

Merged
merged 12 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange
import de.rki.coronawarnapp.sharing.ExposureSharingService
import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel
import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.observe2
import de.rki.coronawarnapp.util.ui.viewBindingLazy
Expand All @@ -39,7 +38,6 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le
)

private val settingsViewModel: SettingsViewModel by activityViewModels()
private val submissionViewModel: SubmissionViewModel by activityViewModels()

private val binding: FragmentTestRiskLevelCalculationBinding by viewBindingLazy()

Expand All @@ -51,7 +49,10 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le
}

binding.settingsViewModel = settingsViewModel
binding.submissionViewModel = submissionViewModel

vm.showRiskStatusCard.observe2(this) {
binding.showRiskStatusCard = it
}

binding.buttonRetrieveDiagnosisKeys.setOnClickListener { vm.retrieveDiagnosisKeys() }
binding.buttonProvideKeyViaQr.setOnClickListener { vm.scanLocalQRCodeAndProvide() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange
import de.rki.coronawarnapp.storage.AppDatabase
import de.rki.coronawarnapp.storage.LocalData
import de.rki.coronawarnapp.storage.RiskLevelRepository
import de.rki.coronawarnapp.storage.SubmissionRepository
import de.rki.coronawarnapp.task.TaskController
import de.rki.coronawarnapp.task.common.DefaultTaskRequest
import de.rki.coronawarnapp.task.submitBlocking
import de.rki.coronawarnapp.ui.tracing.card.TracingCardStateProvider
import de.rki.coronawarnapp.util.KeyFileHelper
import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.di.AppContext
import de.rki.coronawarnapp.util.di.AppInjector
Expand All @@ -35,6 +37,7 @@ import de.rki.coronawarnapp.util.ui.SingleLiveEvent
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.withContext
import timber.log.Timber
Expand Down Expand Up @@ -63,6 +66,9 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
val riskLevelResetEvent = SingleLiveEvent<Unit>()
val apiKeysProvidedEvent = SingleLiveEvent<DiagnosisKeyProvidedEvent>()
val riskScoreState = MutableLiveData<RiskScoreState>(RiskScoreState())
val showRiskStatusCard = SubmissionRepository.deviceUIStateFlow.map {
it.withSuccess(false) { true }
}.asLiveData(dispatcherProvider.Default)

val tracingCardState = tracingCardStateProvider.state
.sample(150L)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" />

<variable
name="submissionViewModel"
type="de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel" />
name="showRiskStatusCard"
type="Boolean" />

<variable
name="settingsViewModel"
Expand Down Expand Up @@ -48,7 +48,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_normal"
android:visibility="@{FormatterSubmissionHelper.formatShowRiskStatusCard(submissionViewModel.deviceUiState)}"
gone="@{showRiskStatusCard == null || !showRiskStatusCard}"
android:focusable="true"
android:backgroundTint="@{tracingCard.getRiskInfoContainerBackgroundTint(context)}"
android:backgroundTintMode="src_over">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ open class CwaSuccessResponseWithCodeMismatchNotSupportedError(statusCode: Int)
open class CwaInformationalNotSupportedError(statusCode: Int) : CwaWebException(statusCode)
open class CwaRedirectNotSupportedError(statusCode: Int) : CwaWebException(statusCode)

class CwaUnknownHostException : CwaWebException(901)
class BadRequestException : CwaClientError(400)
class UnauthorizedException : CwaClientError(401)
class ForbiddenException : CwaClientError(403)
Expand All @@ -44,5 +43,6 @@ class ServiceUnavailableException : CwaServerError(503)
class GatewayTimeoutException : CwaServerError(504)
class HTTPVersionNotSupported : CwaServerError(505)
class NetworkAuthenticationRequiredException : CwaServerError(511)
class CwaUnknownHostException : CwaServerError(597)
class NetworkReadTimeoutException : CwaServerError(598)
class NetworkConnectTimeoutException : CwaServerError(599)
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import de.rki.coronawarnapp.exception.http.UnauthorizedException
import de.rki.coronawarnapp.exception.http.UnsupportedMediaTypeException
import okhttp3.Interceptor
import okhttp3.Response
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.HttpsURLConnection

Expand Down Expand Up @@ -66,6 +67,8 @@ class HttpErrorParser : Interceptor {
throw CwaWebException(code)
}
}
} catch (err: SocketTimeoutException) {
throw NetworkConnectTimeoutException()
} catch (err: UnknownHostException) {
throw CwaUnknownHostException()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package de.rki.coronawarnapp.storage

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asLiveData
import de.rki.coronawarnapp.exception.ExceptionCategory
import de.rki.coronawarnapp.exception.NoRegistrationTokenSetException
import de.rki.coronawarnapp.exception.http.CwaWebException
import de.rki.coronawarnapp.exception.reporting.report
import de.rki.coronawarnapp.service.submission.SubmissionService
import de.rki.coronawarnapp.ui.submission.ApiRequestState
import de.rki.coronawarnapp.util.DeviceUIState
import de.rki.coronawarnapp.util.Event
import de.rki.coronawarnapp.util.NetworkRequestWrapper
import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess
import de.rki.coronawarnapp.util.di.AppInjector
import de.rki.coronawarnapp.util.formatter.TestResult
import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
Expand All @@ -26,15 +23,12 @@ object SubmissionRepository {
AppInjector.component.appScope
}

val uiStateStateFlowInternal = MutableStateFlow(ApiRequestState.IDLE)
val uiStateStateFlow: Flow<ApiRequestState> = uiStateStateFlowInternal
val uiStateState: LiveData<ApiRequestState> = uiStateStateFlow.asLiveData()

private val testResultReceivedDateFlowInternal = MutableStateFlow(Date())
val testResultReceivedDateFlow: Flow<Date> = testResultReceivedDateFlowInternal

private val deviceUIStateFlowInternal = MutableStateFlow(DeviceUIState.UNPAIRED)
val deviceUIStateFlow: Flow<DeviceUIState> = deviceUIStateFlowInternal
private val deviceUIStateFlowInternal =
MutableStateFlow<NetworkRequestWrapper<DeviceUIState, Throwable>>(NetworkRequestWrapper.RequestIdle)
val deviceUIStateFlow: Flow<NetworkRequestWrapper<DeviceUIState, Throwable>> = deviceUIStateFlowInternal

private val testResultFlow = MutableStateFlow<TestResult?>(null)

Expand Down Expand Up @@ -80,37 +74,33 @@ object SubmissionRepository {
LocalData.teletan(teletan)
}

private val uiStateErrorInternal = MutableLiveData<Event<CwaWebException>>(null)
val uiStateError: LiveData<Event<CwaWebException>> = uiStateErrorInternal

// TODO this should be more UI agnostic
fun refreshDeviceUIState(refreshTestResult: Boolean = true) {
var refresh = refreshTestResult

deviceUIStateFlowInternal.value.let {
deviceUIStateFlowInternal.value.withSuccess {
if (it != DeviceUIState.PAIRED_NO_RESULT && it != DeviceUIState.UNPAIRED) {
refresh = false
Timber.d("refreshDeviceUIState: Change refresh, state ${it.name} doesn't require refresh")
}
}

uiStateStateFlowInternal.value = ApiRequestState.STARTED
deviceUIStateFlowInternal.value = NetworkRequestWrapper.RequestStarted

appScope.launch {
try {
refreshUIState(refresh)
uiStateStateFlowInternal.value = ApiRequestState.SUCCESS
} catch (err: CwaWebException) {
uiStateErrorInternal.postValue(Event(err))
uiStateStateFlowInternal.value = ApiRequestState.FAILED
deviceUIStateFlowInternal.value = NetworkRequestWrapper.RequestFailed(err)
} catch (err: Exception) {
deviceUIStateFlowInternal.value = NetworkRequestWrapper.RequestFailed(err)
err.report(ExceptionCategory.INTERNAL)
}
}
}

fun reset() {
uiStateStateFlowInternal.value = ApiRequestState.IDLE
deviceUIStateFlowInternal.value = DeviceUIState.UNPAIRED
deviceUIStateFlowInternal.value = NetworkRequestWrapper.RequestIdle
}

// TODO this should be more UI agnostic
Expand All @@ -132,6 +122,6 @@ object SubmissionRepository {
}
}
}
deviceUIStateFlowInternal.value = uiState
deviceUIStateFlowInternal.value = NetworkRequestWrapper.RequestSuccessful(uiState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package de.rki.coronawarnapp.ui.main.home
import android.content.Context
import android.graphics.drawable.Drawable
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.ui.submission.ApiRequestState
import de.rki.coronawarnapp.exception.http.CwaServerError
import de.rki.coronawarnapp.util.DeviceUIState
import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_ERROR
import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_NEGATIVE
Expand All @@ -12,72 +12,113 @@ import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE
import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE_TELETAN
import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_REDEEMED
import de.rki.coronawarnapp.util.DeviceUIState.SUBMITTED_FINAL
import de.rki.coronawarnapp.util.NetworkRequestWrapper
import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess

data class SubmissionCardState(
val deviceUiState: DeviceUIState,
val isDeviceRegistered: Boolean,
val uiStateState: ApiRequestState
val deviceUiState: NetworkRequestWrapper<DeviceUIState, Throwable>,
val isDeviceRegistered: Boolean
) {

fun isRiskCardVisible(): Boolean = deviceUiState != PAIRED_POSITIVE &&
deviceUiState != PAIRED_POSITIVE_TELETAN &&
deviceUiState != SUBMITTED_FINAL
fun isRiskCardVisible(): Boolean =
deviceUiState.withSuccess(true) {
when (it) {
PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN, SUBMITTED_FINAL -> false
else -> true
}
}

fun isUnregisteredCardVisible(): Boolean = !isDeviceRegistered

fun isFetchingCardVisible(): Boolean =
isDeviceRegistered && (uiStateState == ApiRequestState.STARTED || uiStateState == ApiRequestState.FAILED)
isDeviceRegistered && when (deviceUiState) {
is NetworkRequestWrapper.RequestFailed -> deviceUiState.error is CwaServerError
is NetworkRequestWrapper.RequestStarted -> true
else -> false
}

fun isFailedCardVisible(): Boolean =
isDeviceRegistered && uiStateState == ApiRequestState.SUCCESS && deviceUiState == PAIRED_REDEEMED
isDeviceRegistered && when (deviceUiState) {
is NetworkRequestWrapper.RequestFailed -> deviceUiState.error !is CwaServerError
is NetworkRequestWrapper.RequestSuccessful -> deviceUiState.data == PAIRED_REDEEMED
else -> false
}

fun isPositiveSubmissionCardVisible(): Boolean = uiStateState == ApiRequestState.SUCCESS &&
(deviceUiState == PAIRED_POSITIVE ||
deviceUiState == PAIRED_POSITIVE_TELETAN)
fun isPositiveSubmissionCardVisible(): Boolean =
deviceUiState.withSuccess(false) {
when (it) {
PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN -> true
else -> false
}
}

fun isSubmissionDoneCardVisible(): Boolean =
uiStateState == ApiRequestState.SUCCESS && deviceUiState == SUBMITTED_FINAL
when (deviceUiState) {
is NetworkRequestWrapper.RequestSuccessful -> deviceUiState.data == SUBMITTED_FINAL
else -> false
}

fun isContentCardVisible(): Boolean =
uiStateState == ApiRequestState.SUCCESS && (deviceUiState == PAIRED_ERROR ||
deviceUiState == PAIRED_NEGATIVE ||
deviceUiState == PAIRED_NO_RESULT)
deviceUiState.withSuccess(false) {
when (it) {
PAIRED_ERROR, PAIRED_NEGATIVE, PAIRED_NO_RESULT -> true
else -> false
}
}

fun getContentCardTitleText(c: Context): String = when (deviceUiState) {
PAIRED_ERROR, PAIRED_REDEEMED, PAIRED_NEGATIVE -> R.string.submission_status_card_title_available
PAIRED_NO_RESULT -> R.string.submission_status_card_title_pending
else -> R.string.submission_status_card_title_pending
}.let { c.getString(it) }
fun getContentCardTitleText(c: Context): String =
deviceUiState.withSuccess(R.string.submission_status_card_title_pending) {
when (it) {
PAIRED_ERROR, PAIRED_REDEEMED, PAIRED_NEGATIVE -> R.string.submission_status_card_title_available
PAIRED_NO_RESULT -> R.string.submission_status_card_title_pending
else -> R.string.submission_status_card_title_pending
}
}.let { c.getString(it) }

fun getContentCardSubTitleText(c: Context): String = when (deviceUiState) {
PAIRED_NEGATIVE -> R.string.submission_status_card_subtitle_negative
PAIRED_ERROR, PAIRED_REDEEMED -> R.string.submission_status_card_subtitle_invalid
else -> null
}?.let { c.getString(it) } ?: ""
fun getContentCardSubTitleText(c: Context): String =
deviceUiState.withSuccess(null) {
when (it) {
PAIRED_NEGATIVE -> R.string.submission_status_card_subtitle_negative
PAIRED_ERROR, PAIRED_REDEEMED -> R.string.submission_status_card_subtitle_invalid
else -> null
}
}?.let { c.getString(it) } ?: ""

fun getContentCardSubTitleTextColor(c: Context): Int = when (deviceUiState) {
PAIRED_NEGATIVE -> R.color.colorTextSemanticGreen
PAIRED_ERROR, PAIRED_REDEEMED -> R.color.colorTextSemanticNeutral
else -> R.color.colorTextPrimary1
}.let { c.getColor(it) }
fun getContentCardSubTitleTextColor(c: Context): Int =
deviceUiState.withSuccess(R.color.colorTextPrimary1) {
when (it) {
PAIRED_NEGATIVE -> R.color.colorTextSemanticGreen
PAIRED_ERROR, PAIRED_REDEEMED -> R.color.colorTextSemanticNeutral
else -> R.color.colorTextPrimary1
}
}.let { c.getColor(it) }

fun isContentCardStatusTextVisible(): Boolean = when (deviceUiState) {
PAIRED_NEGATIVE, PAIRED_REDEEMED, PAIRED_ERROR -> true
else -> false
}
fun isContentCardStatusTextVisible(): Boolean =
deviceUiState.withSuccess(false) {
when (it) {
PAIRED_NEGATIVE, PAIRED_REDEEMED, PAIRED_ERROR -> true
else -> false
}
}

fun getContentCardBodyText(c: Context): String = when (deviceUiState) {
PAIRED_ERROR, PAIRED_REDEEMED -> R.string.submission_status_card_body_invalid
PAIRED_NEGATIVE -> R.string.submission_status_card_body_negative
PAIRED_NO_RESULT -> R.string.submission_status_card_body_pending
else -> R.string.submission_status_card_body_pending
}.let { c.getString(it) }
fun getContentCardBodyText(c: Context): String =
deviceUiState.withSuccess(R.string.submission_status_card_body_pending) {
when (it) {
PAIRED_ERROR, PAIRED_REDEEMED -> R.string.submission_status_card_body_invalid
PAIRED_NEGATIVE -> R.string.submission_status_card_body_negative
PAIRED_NO_RESULT -> R.string.submission_status_card_body_pending
else -> R.string.submission_status_card_body_pending
}
}.let { c.getString(it) }

fun getContentCardIcon(c: Context): Drawable? = when (deviceUiState) {
PAIRED_NO_RESULT -> R.drawable.ic_main_illustration_pending
PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN -> R.drawable.ic_main_illustration_pending
PAIRED_NEGATIVE -> R.drawable.ic_main_illustration_negative
PAIRED_ERROR, PAIRED_REDEEMED -> R.drawable.ic_main_illustration_invalid
else -> R.drawable.ic_main_illustration_invalid
}.let { c.getDrawable(it) }
fun getContentCardIcon(c: Context): Drawable? =
deviceUiState.withSuccess(R.drawable.ic_main_illustration_invalid) {
when (it) {
PAIRED_NO_RESULT -> R.drawable.ic_main_illustration_pending
PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN -> R.drawable.ic_main_illustration_pending
PAIRED_NEGATIVE -> R.drawable.ic_main_illustration_negative
PAIRED_ERROR, PAIRED_REDEEMED -> R.drawable.ic_main_illustration_invalid
else -> R.drawable.ic_main_illustration_invalid
}
}.let { c.getDrawable(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package de.rki.coronawarnapp.ui.main.home
import dagger.Reusable
import de.rki.coronawarnapp.storage.LocalData
import de.rki.coronawarnapp.storage.SubmissionRepository
import de.rki.coronawarnapp.ui.submission.ApiRequestState
import de.rki.coronawarnapp.util.DeviceUIState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onCompletion
Expand All @@ -17,12 +15,10 @@ import javax.inject.Inject
class SubmissionCardsStateProvider @Inject constructor() {

val state: Flow<SubmissionCardState> = combine(
SubmissionRepository.deviceUIStateFlow,
SubmissionRepository.uiStateStateFlow
SubmissionRepository.deviceUIStateFlow
) { args ->
SubmissionCardState(
deviceUiState = args[0] as DeviceUIState,
uiStateState = args[1] as ApiRequestState,
deviceUiState = args[0],
isDeviceRegistered = LocalData.registrationToken() != null
)
}
Expand Down