Commit 312641f7 authored by Harri Kirik's avatar Harri Kirik
Browse files

COVAPP-421: Remove the 2nd day data post related UI

parent 3dc246e9
package org.dpppt.android.app.domain.usecase.data
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import org.dpppt.android.app.domain.entity.MyDataState
import org.dpppt.android.app.storage.SecureStorage
import org.junit.Test
import org.junit.runner.RunWith
import java.time.LocalDateTime
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class CheckIsMyDataDeletableUseCaseTest {
@Test
fun execute_SHOULD_BE_SAFE_TO_DELETE_MY_DATA_WHEN_TIMESTAMP_IS_NULL() {
val secureStorage: SecureStorage = mock {
on { confirmInfectionResultTimestamp } doReturn null
}
val useCase = CheckIsMyDataDeletableUseCase(secureStorage)
assertEquals(MyDataState.SafeToDelete, useCase.execute())
}
@Test
fun execute_SHOULD_BE_MARKED_AS_DATA_SENT_WHEN_24_HOURS_HAVE_PASSED() {
val secureStorage: SecureStorage = mock {
on { confirmInfectionResultTimestamp } doReturn LocalDateTime.now().minusDays(2)
}
val useCase = CheckIsMyDataDeletableUseCase(secureStorage)
assertEquals(MyDataState.DataSent, useCase.execute())
}
@Test
fun execute_SHOULD_NOT_BE_SAFE_TO_DELETE_MY_DATA_WHEN_LESS_THAN_24_HOURS_HAVE_PASSED() {
val secureStorage: SecureStorage = mock {
on { confirmInfectionResultTimestamp } doReturn LocalDateTime.now().minusDays(1).plusMinutes(1)
}
val useCase = CheckIsMyDataDeletableUseCase(secureStorage)
assertEquals(MyDataState.NotSafeToDelete, useCase.execute())
}
}
......@@ -38,7 +38,6 @@ import org.dpppt.android.sdk.InfectionStatus
import org.dpppt.android.sdk.TracingStatus
import timber.log.Timber
import java.text.DateFormat
import java.time.LocalDateTime
import java.util.Date
import javax.inject.Inject
import org.dpppt.android.app.BuildConfig as AppBuildConfig
......@@ -271,11 +270,6 @@ class DebugMenuFragment : MvvmFragment(), ViewBindingHolder<FragmentDebugBinding
}
private fun setDebugAppState(debugAppState: DebugAppState) {
if (debugAppState == DebugAppState.REPORTED_EXPOSED) {
secureStorage.confirmInfectionResultTimestamp = LocalDateTime.now()
} else {
secureStorage.removeConfirmInfectionResultTimestamp()
}
(tracingViewModel.tracingStatusInterface as TracingStatusWrapper).setDebugAppState(context, debugAppState)
}
......
......@@ -85,12 +85,5 @@ class ConfirmationDialogFragment : DialogFragment() {
context.getString(R.string.btn_install)
}
)
fun confirmDataDeletionDialog(context: Context) = newInstance(
title = context.getString(R.string.my_data_dialog_title),
message = context.getString(R.string.my_data_dialog_description),
positiveButton = context.getString(R.string.my_data_dialog_btn_delete),
negativeButton = context.getString(R.string.my_data_dialog_btn_cancel)
)
}
}
package org.dpppt.android.app.domain.entity
sealed class MyDataState {
object SafeToDelete : MyDataState()
object NotSafeToDelete : MyDataState()
object DataSent : MyDataState()
}
package org.dpppt.android.app.domain.usecase.data
import dagger.Reusable
import org.dpppt.android.app.domain.entity.MyDataState
import org.dpppt.android.app.storage.SecureStorage
import java.time.LocalDateTime
import javax.inject.Inject
@Reusable
class CheckIsMyDataDeletableUseCase @Inject constructor(
private val secureStorage: SecureStorage
) {
fun execute(): MyDataState {
val timestamp = secureStorage.confirmInfectionResultTimestamp
return when {
timestamp == null -> {
MyDataState.SafeToDelete
}
timestamp.isAfter(LocalDateTime.now().minusDays(1)) -> {
MyDataState.NotSafeToDelete
}
else -> {
MyDataState.DataSent
}
}
}
}
......@@ -132,16 +132,6 @@ open class CardView : MaterialCardView {
descriptionBackgroundColor = Color.TRANSPARENT
)
fun myDataCardExpandedWithNotSafeToDeleteWarning(context: Context) = State(
headerColor = Color.TRANSPARENT,
titleTextColor = ContextCompat.getColor(context, R.color.primary),
titleText = context.resources.getString(R.string.home_my_data_title),
descriptionText = context.resources.getString(R.string.home_my_data_description_not_safe_to_delete),
chevronIconColor = ContextCompat.getColor(context, R.color.primary),
chevronIcon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_forward_large),
descriptionBackgroundColor = Color.TRANSPARENT
)
fun myDataCardExpandedWithDetectionSuspendedInfo(context: Context) = State(
headerColor = Color.TRANSPARENT,
titleTextColor = ContextCompat.getColor(context, R.color.primary),
......
......@@ -25,7 +25,6 @@ import org.dpppt.android.app.common.util.setTextWithBoldSpans
import org.dpppt.android.app.databinding.FragmentHomeBinding
import org.dpppt.android.app.di.Injector
import org.dpppt.android.app.domain.entity.AppTracingStatus
import org.dpppt.android.app.domain.entity.MyDataState
import org.dpppt.android.app.domain.entity.enums.SettingsScrollDestination
import org.dpppt.android.app.main.model.InfectedStatus
import org.dpppt.android.app.util.startActivitySafely
......@@ -77,7 +76,6 @@ class HomeFragment : MvvmFragment(R.layout.fragment_home), ViewBindingHolder<Fra
viewModel.infectedStatus.onEachNotNull(::showInfectedStatus)
viewModel.tracing.onEachNotNull(::showTracingStatusInfo)
viewModel.action.onEachEvent(::handleAction)
viewModel.isMyDataDeletable.onEachNotNull(::showIsMyDataDeletable)
}
private fun handleAction(action: HomeViewModel.Action) {
......@@ -116,6 +114,8 @@ class HomeFragment : MvvmFragment(R.layout.fragment_home), ViewBindingHolder<Fra
infectionAbroadCard.visibility = View.VISIBLE
infectionAbroadCard.setState(CardView.State.notInfectedAbroad(requireContext()))
infectionAbroadCard.setOnClickListener { viewModel.gotSickAbroadClicked() }
// My data is collapsed
myDataCard.setState(CardView.State.myDataCardCollapsed(requireContext()))
}
private fun FragmentHomeBinding.setInfectionCardsToInfected() {
......@@ -125,20 +125,8 @@ class HomeFragment : MvvmFragment(R.layout.fragment_home), ViewBindingHolder<Fra
infectionAtHomeCard.setDescriptionOnClickListener { viewModel.covidInstructionsClicked() }
// Abroad card not visible
infectionAbroadCard.visibility = View.GONE
}
private fun showIsMyDataDeletable(state: MyDataState) = with(requireBinding()) {
when (state) {
is MyDataState.NotSafeToDelete -> {
myDataCard.setState(CardView.State.myDataCardExpandedWithNotSafeToDeleteWarning(requireContext()))
}
is MyDataState.SafeToDelete -> {
myDataCard.setState(CardView.State.myDataCardCollapsed(requireContext()))
}
is MyDataState.DataSent -> {
myDataCard.setState(CardView.State.myDataCardExpandedWithDetectionSuspendedInfo(requireContext()))
}
}.exhaustive
// My data is expanded
myDataCard.setState(CardView.State.myDataCardExpandedWithDetectionSuspendedInfo(requireContext()))
}
private fun showInfectedStatus(infectedStatus: InfectedStatus) = with(requireBinding()) {
......
......@@ -9,9 +9,7 @@ import mobi.lab.mvvm.Event
import org.dpppt.android.app.common.util.asLiveData
import org.dpppt.android.app.common.util.backgroundToMain
import org.dpppt.android.app.domain.entity.AppTracingStatus
import org.dpppt.android.app.domain.entity.MyDataState
import org.dpppt.android.app.domain.usecase.contacts.GetInfectedStatusUseCase
import org.dpppt.android.app.domain.usecase.data.CheckIsMyDataDeletableUseCase
import org.dpppt.android.app.domain.usecase.dp3t.GaenSyncUseCase
import org.dpppt.android.app.domain.usecase.dp3t.IsTracingWorkingUseCase
import org.dpppt.android.app.domain.usecase.infopage.CreateCovidInstructionsPageUrlUseCase
......@@ -29,7 +27,6 @@ class HomeViewModel @Inject constructor(
private val createCovidInstructionsPageUrlUseCase: CreateCovidInstructionsPageUrlUseCase,
private val syncUseCase: GaenSyncUseCase,
private val isTracingWorkingUseCase: IsTracingWorkingUseCase,
private val checkIsMyDataDeletableUseCase: CheckIsMyDataDeletableUseCase,
private val createGotSickAbroadPageUrlUseCase: CreateGotSickAbroadPageUrlUseCase
) : ViewModel() {
......@@ -39,9 +36,6 @@ class HomeViewModel @Inject constructor(
private val _infectedStatus = MutableLiveData<InfectedStatus>()
val infectedStatus: LiveData<InfectedStatus> = _infectedStatus
private val _isMyDataDeletable = MutableLiveData<MyDataState>()
val isMyDataDeletable = _isMyDataDeletable.asLiveData()
private val _action = MutableLiveData<Event<Action>>()
val action = _action.asLiveData()
......@@ -55,7 +49,6 @@ class HomeViewModel @Inject constructor(
status.removeObserver(tracingStatusObserver)
status.observeForever(tracingStatusObserver)
startSync()
_isMyDataDeletable.value = checkIsMyDataDeletableUseCase.execute()
}
fun onRefresh() {
......
......@@ -13,19 +13,16 @@ import org.dpppt.android.app.domain.entity.enums.PatientPortalResult
import org.dpppt.android.app.domain.usecase.dp3t.GaenSendIAmInfectedUseCase
import org.dpppt.android.app.domain.usecase.infection.request.DeleteConfirmInfectionRequestUseCase
import org.dpppt.android.app.domain.usecase.infection.request.GetActiveConfirmInfectionRequestUseCase
import org.dpppt.android.app.storage.SecureStorage
import org.dpppt.android.app.util.dispose
import org.dpppt.android.sdk.internal.backend.StatusCodeException
import timber.log.Timber
import java.time.LocalDateTime
import java.util.concurrent.CancellationException
import javax.inject.Inject
class ConfirmInfectionResultViewModel @Inject constructor(
private val deleteInfectionRequestUseCase: DeleteConfirmInfectionRequestUseCase,
private val getActiveRequestUseCase: GetActiveConfirmInfectionRequestUseCase,
private val gaenSendIAmInfectedUseCase: GaenSendIAmInfectedUseCase,
private val secureStorage: SecureStorage
private val gaenSendIAmInfectedUseCase: GaenSendIAmInfectedUseCase
) : ViewModel() {
private val _action = MutableLiveData<Event<Action>>()
......@@ -64,7 +61,6 @@ class ConfirmInfectionResultViewModel @Inject constructor(
Timber.d("onSendInfectedStatusSuccess")
_state.value = UiState.Done
deleteInfectionRequestUseCase.execute()
secureStorage.confirmInfectionResultTimestamp = LocalDateTime.now()
}
private fun onSendInfectedStatusError(error: Throwable) {
......
......@@ -10,7 +10,6 @@ import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import mobi.lab.mvvm.MvvmFragment
import org.dpppt.android.app.R
import org.dpppt.android.app.common.dialog.ConfirmationDialogFragment
import org.dpppt.android.app.common.dialog.DialogBusEvent
import org.dpppt.android.app.common.dialog.DialogUtil
import org.dpppt.android.app.common.dialog.ErrorGenericDialogFragment
......@@ -40,7 +39,7 @@ class MyDataFragment : MvvmFragment(R.layout.fragment_my_data), ViewBindingHolde
Injector.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return createBinding(FragmentMyDataBinding.inflate(inflater), this)
}
......@@ -71,12 +70,6 @@ class MyDataFragment : MvvmFragment(R.layout.fragment_my_data), ViewBindingHolde
event.isFor(TAG_DIALOG_ERROR, DialogBusEvent.Action.BUTTON_POSITIVE) -> {
viewModel.onDeleteErrorReturnHomeClicked()
}
event.isFor(TAG_CONFIRM_DATA_DELETION_DIALOG, DialogBusEvent.Action.BUTTON_POSITIVE) -> {
viewModel.onDeleteData()
}
event.isFor(TAG_CONFIRM_DATA_DELETION_DIALOG, DialogBusEvent.Action.BUTTON_NEGATIVE) -> {
DialogUtil.dismiss(this, TAG_CONFIRM_DATA_DELETION_DIALOG)
}
}
}
......@@ -85,7 +78,6 @@ class MyDataFragment : MvvmFragment(R.layout.fragment_my_data), ViewBindingHolde
when (action) {
is MyDataViewModel.Action.ShowDeleteSuccess -> showDeleteSuccess()
is MyDataViewModel.Action.ShowDeleteError -> showDeleteError(action.error)
is MyDataViewModel.Action.ShowDataDeletionWarningDialog -> showDataDeletionWarningDialog()
is MyDataViewModel.Action.NavigateToHomeRestart -> findNavController().navigate(MyDataFragmentDirections.homeRestart())
is MyDataViewModel.Action.OpenPrivacyPolicyUrl -> startActivitySafely(
Intent(Intent.ACTION_VIEW).setData(
......@@ -113,10 +105,6 @@ class MyDataFragment : MvvmFragment(R.layout.fragment_my_data), ViewBindingHolde
)
}
private fun showDataDeletionWarningDialog() {
DialogUtil.show(this, ConfirmationDialogFragment.confirmDataDeletionDialog(requireContext()), TAG_CONFIRM_DATA_DELETION_DIALOG)
}
private fun initPrivacyPolicyText() {
requireBinding().textAppDataPrivacyPolicy.setTextWithClickableSpan(
getString(R.string.my_data_privacy_policy),
......@@ -128,6 +116,5 @@ class MyDataFragment : MvvmFragment(R.layout.fragment_my_data), ViewBindingHolde
companion object {
private const val TAG_DIALOG_ERROR = "MyDataFragment.TAG_DIALOG_ERROR"
private const val TAG_CONFIRM_DATA_DELETION_DIALOG = "MyDataFragment.TAG_CONFIRM_DATA_DELETION_DIALOG"
}
}
......@@ -7,19 +7,14 @@ import mobi.lab.mvvm.Event
import org.dpppt.android.app.common.util.asLiveData
import org.dpppt.android.app.common.util.backgroundToMain
import org.dpppt.android.app.domain.entity.DomainException
import org.dpppt.android.app.domain.entity.MyDataState
import org.dpppt.android.app.domain.usecase.data.CheckIsMyDataDeletableUseCase
import org.dpppt.android.app.domain.usecase.data.DeleteLocalDataUseCase
import org.dpppt.android.app.domain.usecase.infopage.CreatePrivacyPolicyUrlUseCase
import org.dpppt.android.app.storage.SecureStorage
import org.dpppt.android.app.util.dispose
import javax.inject.Inject
class MyDataViewModel @Inject constructor(
private val createPrivacyPolicyUrlUseCase: CreatePrivacyPolicyUrlUseCase,
private val deleteUseCase: DeleteLocalDataUseCase,
private val secureStorage: SecureStorage,
private val checkIsMyDataDeletableUseCase: CheckIsMyDataDeletableUseCase
private val deleteUseCase: DeleteLocalDataUseCase
) : ViewModel() {
private val _action = MutableLiveData<Event<Action>>()
......@@ -41,12 +36,7 @@ class MyDataViewModel @Inject constructor(
}
fun onDeleteClicked() {
val state = checkIsMyDataDeletableUseCase.execute()
if (state == MyDataState.NotSafeToDelete) {
_action.value = Event(Action.ShowDataDeletionWarningDialog)
} else {
onDeleteData()
}
onDeleteData()
}
fun onDeleteData() {
......@@ -66,7 +56,6 @@ class MyDataViewModel @Inject constructor(
private fun onDeleteSuccess() {
_action.value = Event(Action.ShowDeleteSuccess)
secureStorage.removeConfirmInfectionResultTimestamp()
restartToHome()
}
......@@ -77,7 +66,6 @@ class MyDataViewModel @Inject constructor(
sealed class Action {
object ShowDeleteSuccess : Action()
data class ShowDeleteError(val error: DomainException) : Action()
object ShowDataDeletionWarningDialog : Action()
data class OpenPrivacyPolicyUrl(val url: String) : Action()
object NavigateToHomeRestart : Action()
}
......
......@@ -22,8 +22,6 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import timber.log.Timber;
......@@ -37,7 +35,6 @@ public class SecureStorage implements ConfirmInfectionRequestStorage, DeletableL
private static final String KEY_CROSS_COUNTRY_NOTIFIED = "cross_country_notified";
private static final String KEY_LAST_SHOWN_CONTACT_ID = "last_shown_contact_id";
private static final String KEY_LANGUAGE_CODE = "language_code";
private static final String KEY_CONFIRM_INFECTION_RESULT_TIMESTAMP = "confirm_infection_result_timestamp";
private static SecureStorage instance;
private SharedPreferences prefs;
......@@ -95,22 +92,6 @@ public class SecureStorage implements ConfirmInfectionRequestStorage, DeletableL
prefs.edit().putString(KEY_LANGUAGE_CODE, languageCode).apply();
}
public LocalDateTime getConfirmInfectionResultTimestamp() {
long timestamp = prefs.getLong(KEY_CONFIRM_INFECTION_RESULT_TIMESTAMP, -1);
if (timestamp == -1) {
return null;
}
return LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.UTC);
}
public void setConfirmInfectionResultTimestamp(LocalDateTime date) {
prefs.edit().putLong(KEY_CONFIRM_INFECTION_RESULT_TIMESTAMP, date.toEpochSecond(ZoneOffset.UTC)).apply();
}
public void removeConfirmInfectionResultTimestamp() {
prefs.edit().remove(KEY_CONFIRM_INFECTION_RESULT_TIMESTAMP).apply();
}
// New Estonian flavour methods
@Nullable
......
......@@ -187,11 +187,6 @@
<string name="language_russian">"Русский"</string>
<!-- Fuzzy -->
<string name="home_my_data_description_not_safe_to_delete">"Kõik anonüümsed koodid ei ole veel teavituseks edastatud. Kõigile lähikontaktidele teavituse saatmiseks palun oodake andmete kustutamisega 24 tundi."</string>
<string name="my_data_dialog_title">"Anonüümsete koodide kustutamine"</string>
<string name="my_data_dialog_description">"Kõik anonüümsed koodid ei ole veel teavituseks edastatud. Kõigile lähikontaktidele teavituse saatmiseks palun oodake andmete kustutamisega 24 tundi."</string>
<string name="my_data_dialog_btn_delete">"Kustuta"</string>
<string name="my_data_dialog_btn_cancel">"Tühista"</string>
<string name="home_my_data_description_exposure_detection_suspended">"Lähikontaktide salvestamine peatatud. Taaskäivitamiseks kustutage vanad andmed."</string>
<string name="home_status_app_data_submitted">"Andmed edastatud"</string>
<string name="debug_sdk_state_synced_not_applicable">"-"</string>
......
......@@ -189,11 +189,6 @@
<string name="language_russian">"Русский"</string>
<!-- Fuzzy -->
<string name="home_my_data_description_not_safe_to_delete">"Все анонимные коды ещё не доставлены к оповещению. Для отправки уведомления всем близким контактам, пожалуйста, подождите с удалением данных 24 часа."</string>
<string name="my_data_dialog_title">"Удаление анонимных кодов"</string>
<string name="my_data_dialog_description">"Все анонимные коды ещё не доставлены к оповещению. Для отправки уведомления всем близким контактам, пожалуйста, подождите с удалением данных 24 часа."</string>
<string name="my_data_dialog_btn_delete">"Yдалить"</string>
<string name="my_data_dialog_btn_cancel">"Oтменить"</string>
<string name="home_my_data_description_exposure_detection_suspended">"Сохранение близких контактов преостановлено. Удалите старые данные, чтобы восстановить обнаружение близких контактов."</string>
<string name="home_status_app_data_submitted">"Данные предоставлены"</string>
<string name="debug_sdk_state_synced_not_applicable">"-"</string>
......
......@@ -187,11 +187,6 @@
<string name="language_russian">"Русский"</string>
<!-- Fuzzy -->
<string name="home_my_data_description_not_safe_to_delete">"Not all anonymous codes have been sent yet for notification. To ensure all close contacts receive a notification, please wait 24 hours before deleting your data."</string>
<string name="my_data_dialog_title">"Deleting anonymous codes"</string>
<string name="my_data_dialog_description">"Not all anonymous codes have been sent yet for notification. To ensure all close contacts receive a notification, please wait 24 hours before deleting your data."</string>
<string name="my_data_dialog_btn_delete">"Delete"</string>
<string name="my_data_dialog_btn_cancel">"Cancel"</string>
<string name="home_my_data_description_exposure_detection_suspended">"Exposure detection suspended. Delete your old data to resume detection."</string>
<string name="home_status_app_data_submitted">"Data submitted"</string>
<string name="debug_sdk_state_synced_not_applicable">"N/A"</string>
......@@ -222,4 +217,4 @@
</plurals>
<string name="home_infection_status_infected">"Notifications sent!"</string>
<string name="home_infection_status_more_info">"More info"</string>
</resources>
\ No newline at end of file
</resources>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment