diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt index 63a39c72f3055e641fbb4e325599a59bae72169b..21a608d5d0a1ecea628872564718107418bba099 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.contactdiary.ui.overview import android.os.Bundle import android.view.View +import androidx.core.app.ShareCompat import androidx.fragment.app.Fragment import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ContactDiaryOverviewAdapter @@ -12,6 +13,7 @@ import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import timber.log.Timber import javax.inject.Inject class ContactDiaryOverviewFragment : Fragment(R.layout.contact_diary_overview_fragment), AutoInject { @@ -55,6 +57,25 @@ class ContactDiaryOverviewFragment : Fragment(R.layout.contact_diary_overview_fr } } } + vm.exportLocationsAndPersons.observe2(this) { + exportLocationsAndPersons(it) + } + } + + private fun exportLocationsAndPersons(exportString: String) { + Timber.d("exportLocationsAndPersons(exportString=$exportString)") + activity?.let { activity -> + val shareIntent = ShareCompat.IntentBuilder + .from(activity) + .setType("text/plain") + .setSubject(getString(R.string.contact_diary_export_subject)) + .setText(exportString) + .createChooserIntent() + + if (shareIntent.resolveActivity(activity.packageManager) != null) { + startActivity(shareIntent) + } + } } private fun setupToolbar() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt index 6f757e6811108917dafffe58a7322fa8a5b457de..07b6fe6aed91f8d3788564aff09417fe7c0dd93e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt @@ -4,19 +4,27 @@ import android.content.Context import android.view.View import android.widget.PopupMenu import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import javax.inject.Inject +// TODO(Remove this useless class) class ContactDiaryOverviewMenu @Inject constructor( private val contactDiaryOverviewFragment: ContactDiaryOverviewFragment ) { private val context: Context = contactDiaryOverviewFragment.requireContext() + private val vm: ContactDiaryOverviewViewModel by contactDiaryOverviewFragment.cwaViewModels { + contactDiaryOverviewFragment.viewModelFactory } + // TODO(Move this to ContactDiaryOverviewFragment) fun showMenuFor(view: View) = PopupMenu(context, view).apply { inflate(R.menu.menu_contact_diary_overview) setOnMenuItemClickListener { when (it.itemId) { R.id.menu_contact_diary_information -> { true } - R.id.menu_contact_diary_export_entries -> { true } + R.id.menu_contact_diary_export_entries -> { + vm.onExportPress(context) + true + } R.id.menu_contact_diary_edit_persons -> { true } R.id.menu_contact_diary_edit_locations -> { true } else -> contactDiaryOverviewFragment.onOptionsItemSelected(it) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt index 1e4e346e8a4653bd70430739630f98fe77713972..cf81612bf0d33fb2b88f2d9c50b4f96a504864e5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.contactdiary.ui.overview +import android.content.Context import androidx.lifecycle.asLiveData import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.R @@ -8,24 +9,33 @@ import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ListItem import de.rki.coronawarnapp.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import org.joda.time.LocalDate import timber.log.Timber +import java.util.Locale class ContactDiaryOverviewViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider, contactDiaryRepository: ContactDiaryRepository -) : CWAViewModel() { +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + val routeToScreen: SingleLiveEvent<ContactDiaryOverviewNavigationEvents> = SingleLiveEvent() + val exportLocationsAndPersons: SingleLiveEvent<String> = SingleLiveEvent() + + private val dates = (0 until DAY_COUNT).map { LocalDate.now().minusDays(it) } - private val dates = flowOf((0 until DAY_COUNT).map { LocalDate.now().minusDays(it) }) + private val locationVisitsFlow = contactDiaryRepository.locationVisits + private val personEncountersFlow = contactDiaryRepository.personEncounters val listItems = combine( - dates, - contactDiaryRepository.locationVisits, - contactDiaryRepository.personEncounters + flowOf(dates), + locationVisitsFlow, + personEncountersFlow ) { dateList, locationVisitList, personEncounterList -> createListItemList(dateList, locationVisitList, personEncounterList) }.asLiveData() @@ -86,6 +96,42 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor( routeToScreen.postValue(ContactDiaryOverviewNavigationEvents.NavigateToContactDiaryDayFragment(listItem.date)) } + fun onExportPress(ctx: Context) { + Timber.d("Exporting person and location entries") + launch { + val locationVisits = locationVisitsFlow + .first() + .groupBy({ it.date }, { it.contactDiaryLocation.locationName }) + + val personEncounters = personEncountersFlow + .first() + .groupBy({ it.date }, { it.contactDiaryPerson.fullName }) + + val sb = StringBuilder() + .appendLine(ctx.getString(R.string.contact_diary_export_intro_one, + dates.last().toFormattedString(), + dates.first().toFormattedString())) + .appendLine(ctx.getString(R.string.contact_diary_export_intro_two)) + .appendLine() + + for (date in dates) { + val dateString = date.toFormattedString() + + // According to tech spec persons first and then locations + personEncounters[date]?.addToStringBuilder(sb, dateString) + locationVisits[date]?.addToStringBuilder(sb, dateString) + } + + exportLocationsAndPersons.postValue(sb.toString()) + } + } + + private fun List<String>.addToStringBuilder(sb: StringBuilder, dateString: String) = sorted() + .forEach { sb.appendLine("$dateString $it") } + + // According to tech spec german locale only + private fun LocalDate.toFormattedString(): String = toString("dd.MM.yyyy", Locale.GERMAN) + @AssistedInject.Factory interface Factory : SimpleCWAViewModelFactory<ContactDiaryOverviewViewModel> diff --git a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml index 23ce37c4533ab8b3a372da8e43a36e1e2bf5fead..652da189cf4c93c501e324847c42ceceb03c00e9 100644 --- a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml @@ -97,5 +97,6 @@ <!-- XHED: Title for the contact diary dialog to delete delete a single location --> <string name="contact_diary_delete_location_title">"Wollen Sie wirklich diesen Ort entfernen?"</string> <!-- XHED: Title for the contact diary dialog to delete delete a single person --> - <string name="contact_diary_delete_person_title">"Wollen Sie wirklich diese Person entfernen?"</string> + <string name="contact_diary_delete_person_title">Wollen Sie wirklich diese Person entfernen?</string> + </resources> diff --git a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml index b57b2e947f7bfe0de23905091001a08a3b1516ad..e64b0e7dadc6659ca65acbe488e708ae4c63588a 100644 --- a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml @@ -93,9 +93,17 @@ <!-- XACT: edit icon description for screen readers --> <string name="contact_diary_edit_icon_content_description">""</string> <!-- XACT: edit icon description for screen readers --> - <string name="contact_diary_delete_icon_content_description">""</string> - <!-- XHED: Title for the contact diary dialog to delete delete a single location --> - <string name="contact_diary_delete_location_title">""</string> - <!-- XHED: Title for the contact diary dialog to delete delete a single person --> - <string name="contact_diary_delete_person_title">""</string> + <string name="contact_diary_delete_icon_content_description">Eintrag löschen</string> + <!-- XHED: Title for the contact diary dialog to delete a single location --> + <string name="contact_diary_delete_location_title">Wollen Sie wirklich diesen Ort entfernen?</string> + <!-- XHED: Title for the contact diary dialog to delete a single person --> + <string name="contact_diary_delete_person_title">Wollen Sie wirklich diese Person entfernen?</string> + + <!-- XHED: Title for the contact journal export email subject --> + <string name="contact_diary_export_subject" translatable="false">"Mein Kontakt-Tagebuch"</string> + <!-- XTXT: Intro text for the contact journal email export part one--> + <string name="contact_diary_export_intro_one" translatable="false">"Kontakte der letzten 14 Tage (%1$s - %2$s)"</string> + <!-- XTXT: Intro text for the contact journal email export part two--> + <string name="contact_diary_export_intro_two" translatable="false">"Die nachfolgende Liste dient dem zuständigen Gesundheitsamt zur Kontaktnachverfolgung gem. § 25 IfSG."</string> + </resources>