From ffa9c3f1675a6dc525ecdea279b33e25d4131ab1 Mon Sep 17 00:00:00 2001
From: Juraj Kusnier <jurajkusnier@users.noreply.github.com>
Date: Fri, 9 Apr 2021 13:47:09 +0200
Subject: [PATCH] Event duplication from QR Code Details (EXPOSUREAPP-6252)
 (#2780)

* Introduce event duplication from QR Code Detail Fragment

* move category to NavigateToDuplicateFragment data class

* update comment

* catch exception from DefaultTraceLocationRepository

Co-authored-by: Mohamed <mohamed.metwalli@sap.com>
Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
Co-authored-by: I502720 <axel.herbstreith@sap.com>
---
 .../create/TraceLocationCreateFragment.kt     |  5 +-
 .../organizer/details/QrCodeDetailFragment.kt | 30 +++---
 .../details/QrCodeDetailNavigationEvents.kt   |  6 +-
 .../details/QrCodeDetailViewModel.kt          | 99 ++++++++-----------
 .../trace_location_organizer_nav_graph.xml    |  3 +
 5 files changed, 71 insertions(+), 72 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/create/TraceLocationCreateFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/create/TraceLocationCreateFragment.kt
index 69f056711..9fa2a6e40 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/create/TraceLocationCreateFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/create/TraceLocationCreateFragment.kt
@@ -47,6 +47,7 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
         }
     )
 
+    @Suppress("NestedBlockDepth")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
@@ -121,8 +122,8 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
                     placeInputEdit.setText(it.address)
                 }
                 viewModel.apply {
-                    begin = LocalDateTime(it.startDate)
-                    end = LocalDateTime(it.endDate)
+                    begin = it.startDate?.let { time -> LocalDateTime(time) }
+                    end = it.endDate?.let { time -> LocalDateTime(time) }
                     checkInLength = Duration.standardMinutes(it.defaultCheckInLengthInMinutes?.toLong() ?: 0L)
                 }
             }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailFragment.kt
index 82344e005..82521d81b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailFragment.kt
@@ -79,24 +79,23 @@ class QrCodeDetailFragment : Fragment(R.layout.trace_location_organizer_qr_code_
                 viewModel.onPrintQrCode()
             }
 
-            root.transitionName = navArgs.traceLocationId.toString()
-        }
-
-        viewModel.qrCodeBitmap.observe2(this) {
-            binding.progressBar.hide()
-            binding.qrCodeImage.apply {
-                val resourceId = RoundedBitmapDrawableFactory.create(resources, it)
-                resourceId.cornerRadius = it.width * 0.1f
-                setImageDrawable(resourceId)
+            qrCodeCloneButton.setOnClickListener {
+                viewModel.duplicateTraceLocation()
             }
+
+            root.transitionName = navArgs.traceLocationId.toString()
         }
 
         viewModel.routeToScreen.observe2(this) {
             when (it) {
                 QrCodeDetailNavigationEvents.NavigateBack -> popBackStack()
 
-                QrCodeDetailNavigationEvents.NavigateToDuplicateFragment -> { /* TODO */
-                }
+                is QrCodeDetailNavigationEvents.NavigateToDuplicateFragment -> doNavigate(
+                    QrCodeDetailFragmentDirections.actionQrCodeDetailFragmentToTraceLocationCreateFragment(
+                        it.category,
+                        it.traceLocation
+                    )
+                )
 
                 is QrCodeDetailNavigationEvents.NavigateToQrCodePosterFragment -> doNavigate(
                     QrCodeDetailFragmentDirections.actionQrCodeDetailFragmentToQrCodePosterFragment(it.locationId)
@@ -134,6 +133,15 @@ class QrCodeDetailFragment : Fragment(R.layout.trace_location_organizer_qr_code_
                 } else {
                     eventDate.isGone = true
                 }
+
+                uiState.bitmap?.let {
+                    binding.progressBar.hide()
+                    binding.qrCodeImage.apply {
+                        val resourceId = RoundedBitmapDrawableFactory.create(resources, it)
+                        resourceId.cornerRadius = it.width * 0.1f
+                        setImageDrawable(resourceId)
+                    }
+                }
             }
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailNavigationEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailNavigationEvents.kt
index 68027a1fd..1939ed098 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailNavigationEvents.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailNavigationEvents.kt
@@ -1,7 +1,11 @@
 package de.rki.coronawarnapp.ui.eventregistration.organizer.details
 
+import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation
+import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.TraceLocationCategory
+
 sealed class QrCodeDetailNavigationEvents {
     object NavigateBack : QrCodeDetailNavigationEvents()
     data class NavigateToQrCodePosterFragment(val locationId: Long) : QrCodeDetailNavigationEvents()
-    object NavigateToDuplicateFragment : QrCodeDetailNavigationEvents()
+    data class NavigateToDuplicateFragment(val traceLocation: TraceLocation, val category: TraceLocationCategory) :
+        QrCodeDetailNavigationEvents()
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt
index 739078b1e..30f3363da 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/organizer/details/QrCodeDetailViewModel.kt
@@ -3,7 +3,6 @@ package de.rki.coronawarnapp.ui.eventregistration.organizer.details
 import android.graphics.Bitmap
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.asLiveData
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -12,15 +11,14 @@ import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation
 import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
+import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.traceLocationCategories
 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.combine
-import kotlinx.coroutines.flow.filterNotNull
 import org.joda.time.Instant
 import timber.log.Timber
+import java.lang.Exception
 
 class QrCodeDetailViewModel @AssistedInject constructor(
     @Assisted private val traceLocationId: Long,
@@ -29,74 +27,38 @@ class QrCodeDetailViewModel @AssistedInject constructor(
     private val traceLocationRepository: DefaultTraceLocationRepository
 ) : CWAViewModel() {
 
-    private val traceLocationFlow = MutableStateFlow<TraceLocation?>(null)
-    private val titleFlow = MutableStateFlow<String?>(null)
-    private val subtitleFlow = MutableStateFlow<String?>(null)
-    private val startTimeFlow = MutableStateFlow<Instant?>(null)
-    private val endTimeFlow = MutableStateFlow<Instant?>(null)
-    private val bitmapLiveData = MutableLiveData<Bitmap>()
+    private var traceLocation: TraceLocation? = null
+    private val mutableUiState = MutableLiveData<UiState>()
+    val uiState: LiveData<UiState>
+        get() = mutableUiState
+    val routeToScreen: SingleLiveEvent<QrCodeDetailNavigationEvents> = SingleLiveEvent()
 
     init {
-
         launch {
-            val traceLocation = traceLocationRepository.traceLocationForId(traceLocationId)
-
-            if (titleFlow.value == null) {
-                titleFlow.value = traceLocation.description
-            }
-
-            if (subtitleFlow.value == null) {
-                subtitleFlow.value = traceLocation.address
-            }
-
-            if (startTimeFlow.value == null) {
-                startTimeFlow.value = traceLocation.startDate
-            }
-
-            if (endTimeFlow.value == null) {
-                endTimeFlow.value = traceLocation.endDate
-            }
-
-            traceLocationFlow.value = traceLocation
-
-            createQrCode(traceLocation)
+            loadTraceLocation()
         }
     }
 
-    val uiState = combine(
-        traceLocationFlow.filterNotNull(),
-        startTimeFlow,
-        endTimeFlow
-    ) { traceLocation, startTime, endTime ->
-        UiState(
-            traceLocation = traceLocation,
-            startInstant = startTime ?: traceLocation.startDate,
-            endInstant = endTime ?: traceLocation.endDate
-        )
-    }.asLiveData()
-
-    data class UiState(
-        private val traceLocation: TraceLocation,
-        private val startInstant: Instant?,
-        private val endInstant: Instant?
-    ) {
-        val description: String get() = traceLocation.description
-        val address: String get() = traceLocation.address
-        val startDateTime: Instant? get() = startInstant
-        val endDateTime: Instant? get() = endInstant
+    private suspend fun loadTraceLocation() {
+        try {
+            traceLocation = traceLocationRepository.traceLocationForId(traceLocationId).also {
+                mutableUiState.postValue(UiState(it))
+                createQrCode(it)
+            }
+        } catch (exception: Exception) {
+            Timber.d(exception, "No location found")
+            exception.report(ExceptionCategory.INTERNAL)
+        }
     }
 
-    val qrCodeBitmap: LiveData<Bitmap> = bitmapLiveData
-    val routeToScreen: SingleLiveEvent<QrCodeDetailNavigationEvents> = SingleLiveEvent()
-
     /**
-     * Creates a QR Code [Bitmap] ,result is delivered by [qrCodeBitmap]
+     * Creates a QR Code [Bitmap] ,result is delivered by [uiState]
      */
     private fun createQrCode(traceLocation: TraceLocation) = launch(context = dispatcher.IO) {
         try {
             val input = traceLocation.locationUrl
             Timber.d("input=$input")
-            bitmapLiveData.postValue(qrCodeGenerator.createQrCode(input))
+            mutableUiState.postValue(UiState(traceLocation, qrCodeGenerator.createQrCode(input)))
         } catch (e: Exception) {
             Timber.d(e, "Qr code creation failed")
             e.report(ExceptionCategory.INTERNAL)
@@ -113,6 +75,27 @@ class QrCodeDetailViewModel @AssistedInject constructor(
         )
     }
 
+    fun duplicateTraceLocation() {
+        traceLocation?.let {
+            val category = traceLocationCategories.find { category -> category.type == it.type }
+            if (category == null) {
+                Timber.e("Category not found, traceLocation = $traceLocation")
+            } else {
+                routeToScreen.postValue(QrCodeDetailNavigationEvents.NavigateToDuplicateFragment(it, category))
+            }
+        }
+    }
+
+    data class UiState(
+        private val traceLocation: TraceLocation,
+        val bitmap: Bitmap? = null
+    ) {
+        val description: String get() = traceLocation.description
+        val address: String get() = traceLocation.address
+        val startDateTime: Instant? get() = traceLocation.startDate
+        val endDateTime: Instant? get() = traceLocation.endDate
+    }
+
     @AssistedFactory
     interface Factory : CWAViewModelFactory<QrCodeDetailViewModel> {
         fun create(
diff --git a/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml
index aa52e6cb3..ae96af2c6 100644
--- a/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml
@@ -82,6 +82,9 @@
         <action
             android:id="@+id/action_qrCodeDetailFragment_to_qrCodePosterFragment"
             app:destination="@id/qrCodePosterFragment" />
+        <action
+            android:id="@+id/action_qrCodeDetailFragment_to_traceLocationCreateFragment"
+            app:destination="@id/traceLocationCreateFragment" />
     </fragment>
 
     <fragment
-- 
GitLab