From 0d90fad8e986a982d08423fc2ba298b433365984 Mon Sep 17 00:00:00 2001
From: Philipp Woessner <64482866+pwoessner@users.noreply.github.com>
Date: Fri, 5 Jun 2020 18:05:04 +0200
Subject: [PATCH] Improved test fragments for risk level calculation (#205)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* added text style for 3 hour mode

* possibility to see all key qr codes, added risk level reset, set transmission risk level for risk level calc

Co-authored-by: Jakob Möller <jakob.moeller@sap.com>
---
 .../rki/coronawarnapp/TestForAPIFragment.kt   | 77 +++++++++++++++----
 .../coronawarnapp/TestRiskLevelCalculation.kt | 41 +++++++++-
 .../res/layout/fragment_test_for_a_p_i.xml    | 10 +--
 .../fragment_test_risk_level_calculation.xml  | 36 ++++++++-
 .../src/main/res/layout/test_qr_code_view.xml |  7 ++
 5 files changed, 145 insertions(+), 26 deletions(-)
 create mode 100644 Corona-Warn-App/src/main/res/layout/test_qr_code_view.xml

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt
index 5eebf8110..8a2247446 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestForAPIFragment.kt
@@ -2,11 +2,14 @@ package de.rki.coronawarnapp
 
 import android.content.Intent
 import android.graphics.Bitmap
+import android.graphics.Color
 import android.os.Bundle
+import android.util.Base64
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
 import android.widget.Switch
 import android.widget.Toast
 import androidx.core.content.pm.PackageInfoCompat
@@ -14,6 +17,8 @@ import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.viewModelScope
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewpager2.widget.ViewPager2
 import com.google.android.gms.common.GoogleApiAvailability
 import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration
 import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
@@ -21,8 +26,11 @@ import com.google.android.gms.nearby.exposurenotification.ExposureSummary
 import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey
 import com.google.gson.Gson
 import com.google.gson.reflect.TypeToken
+import com.google.protobuf.ByteString
+import com.google.zxing.BarcodeFormat
 import com.google.zxing.integration.android.IntentIntegrator
 import com.google.zxing.integration.android.IntentResult
+import com.google.zxing.qrcode.QRCodeWriter
 import de.rki.coronawarnapp.databinding.FragmentTestForAPIBinding
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.ExceptionCategory.INTERNAL
@@ -56,7 +64,6 @@ import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_insert_expo
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_retrieve_exposure_summary
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_tracing_duration_in_retention_period
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.button_tracing_intervals
-import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.image_qr_code
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_attenuation
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_daysSinceLastExposure
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_matchedKeyCount
@@ -64,6 +71,7 @@ import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_sum
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_exposure_summary_summationRiskScore
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_googlePlayServices_version
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.label_my_keys
+import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.qr_code_viewpager
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.test_api_switch_last_three_hours_from_server
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.text_my_keys
 import kotlinx.android.synthetic.main.fragment_test_for_a_p_i.text_scanned_key
@@ -93,6 +101,7 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
     }
 
     private var myExposureKeysJSON: String? = null
+    private var myExposureKeys: List<TemporaryExposureKey>? = mutableListOf()
     private var otherExposureKey: AppleLegacyKeyExchange.Key? = null
     private var otherExposureKeyList = mutableListOf<AppleLegacyKeyExchange.Key>()
 
@@ -103,6 +112,9 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
     // ViewModel for MainActivity
     private val tracingViewModel: TracingViewModel by activityViewModels()
 
+    private lateinit var qrPager: ViewPager2
+    private lateinit var qrPagerAdapter: RecyclerView.Adapter<QRPagerAdapter.QRViewHolder>
+
     // Data and View binding
     private var _binding: FragmentTestForAPIBinding? = null
     private val binding: FragmentTestForAPIBinding get() = _binding!!
@@ -147,15 +159,16 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
         internalExposureNotificationPermissionHelper =
             InternalExposureNotificationPermissionHelper(this, this)
 
+        qrPager = qr_code_viewpager
+        qrPagerAdapter = QRPagerAdapter()
+        qrPager.adapter = qrPagerAdapter
+
         button_api_test_start.setOnClickListener {
             start()
         }
 
         button_api_get_exposure_keys.setOnClickListener {
             getExposureKeys()
-            tracingViewModel.viewModelScope.launch {
-                ExposureSharingService.shareKeysAsBitmap(300, 300, updateQRImageView)
-            }
         }
 
         val last3HoursSwitch = test_api_switch_last_three_hours_from_server as Switch
@@ -257,13 +270,6 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
         getExposureKeys()
     }
 
-    private val updateQRImageView = { bitmap: Bitmap? ->
-        bitmap?.let {
-            image_qr_code.setImageBitmap(bitmap)
-            image_qr_code.visibility = View.VISIBLE
-        }
-    }
-
     private val prettyKey = { key: AppleLegacyKeyExchange.Key ->
         StringBuilder()
             .append("\nKey data: ${key.keyData}")
@@ -439,10 +445,6 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
         )
         label_my_keys.text = myKeysLabelAndCount
         text_my_keys.text = myExposureKeysJSON
-
-        tracingViewModel.viewModelScope.launch {
-            ExposureSharingService.shareKeysAsBitmap(300, 300, updateQRImageView)
-        }
     }
 
     private fun showToast(message: String) {
@@ -461,6 +463,9 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
     override fun onKeySharePermissionGranted(keys: List<TemporaryExposureKey>) {
         myExposureKeysJSON =
             keysToJson(keys)
+        myExposureKeys = keys
+        qrPagerAdapter.notifyDataSetChanged()
+
         updateKeysDisplay()
     }
 
@@ -507,4 +512,46 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
             CONFIG_SCORE
         )
         .build()
+
+    private inner class QRPagerAdapter :
+        RecyclerView.Adapter<QRPagerAdapter.QRViewHolder>() {
+
+        inner class QRViewHolder(val qrCode: ImageView) : RecyclerView.ViewHolder(qrCode)
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QRViewHolder {
+            val imageView = LayoutInflater.from(parent.context)
+                .inflate(R.layout.test_qr_code_view, parent, false) as ImageView
+            return QRViewHolder(imageView)
+        }
+
+        override fun getItemCount(): Int = myExposureKeys?.size ?: 0
+
+        override fun onBindViewHolder(holder: QRViewHolder, position: Int) {
+            myExposureKeys?.get(position)?.let {
+                holder.qrCode.setImageBitmap(bitmapForImage(it))
+            }
+        }
+
+        private fun bitmapForImage(key: TemporaryExposureKey): Bitmap {
+            val key = AppleLegacyKeyExchange.Key.newBuilder()
+                .setKeyData(ByteString.copyFrom(key.keyData))
+                .setRollingPeriod(key.rollingPeriod)
+                .setRollingStartNumber(key.rollingStartIntervalNumber)
+                .build().toByteArray()
+            val bMatrix = QRCodeWriter().encode(
+                Base64.encodeToString(key, Base64.DEFAULT),
+                BarcodeFormat.QR_CODE,
+                300,
+                300
+            )
+            val bmp =
+                Bitmap.createBitmap(bMatrix.width, bMatrix.height, Bitmap.Config.RGB_565)
+            for (x in 0 until bMatrix.width) {
+                for (y in 0 until bMatrix.height) {
+                    bmp.setPixel(x, y, if (bMatrix.get(x, y)) Color.BLACK else Color.WHITE)
+                }
+            }
+            return bmp
+        }
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
index 06ab72e5b..ef572b3a2 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
@@ -6,6 +6,7 @@ import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.EditText
 import android.widget.Toast
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
@@ -26,6 +27,8 @@ import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange
 import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass
 import de.rki.coronawarnapp.service.applicationconfiguration.ApplicationConfigurationService
 import de.rki.coronawarnapp.sharing.ExposureSharingService
+import de.rki.coronawarnapp.storage.AppDatabase
+import de.rki.coronawarnapp.storage.FileStorageHelper
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction
@@ -33,7 +36,10 @@ import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel
 import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel
 import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel
 import de.rki.coronawarnapp.util.KeyFileHelper
+import kotlinx.android.synthetic.main.fragment_test_risk_level_calculation.transmission_number
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import java.io.File
 import java.util.UUID
 import java.util.concurrent.TimeUnit
@@ -94,6 +100,33 @@ class TestRiskLevelCalculation : Fragment() {
             }
         }
 
+        binding.buttonResetRiskLevel.setOnClickListener {
+            tracingViewModel.viewModelScope.launch {
+                withContext(Dispatchers.IO) {
+                    try {
+                        // Database Reset
+                        AppDatabase.getInstance(requireContext()).clearAllTables()
+                        // Delete Database Instance
+                        AppDatabase.resetInstance(requireContext())
+                        // Export File Reset
+                        FileStorageHelper.getAllFilesInKeyExportDirectory().forEach { it.delete() }
+
+                        LocalData.lastCalculatedRiskLevel(RiskLevel.UNDETERMINED.raw)
+                        LocalData.lastSuccessfullyCalculatedRiskLevel(RiskLevel.UNDETERMINED.raw)
+                        LocalData.lastTimeDiagnosisKeysFromServerFetch(null)
+                        LocalData.googleApiToken(null)
+                    } catch (e: java.lang.Exception) {
+                        e.report(ExceptionCategory.INTERNAL)
+                    }
+                }
+                RiskLevelTransaction.start()
+                Toast.makeText(
+                    requireContext(), "Resetted, please fetch diagnosis keys from server again",
+                    Toast.LENGTH_SHORT
+                ).show()
+            }
+        }
+
         startObserving()
     }
 
@@ -151,12 +184,18 @@ class TestRiskLevelCalculation : Fragment() {
 
             val appleKeyList = mutableListOf<AppleLegacyKeyExchange.Key>()
 
+            val text = (transmission_number as EditText).text.toString()
+            var number = 5
+            if (!text.isBlank()) {
+                number = Integer.valueOf(text)
+            }
+
             appleKeyList.add(
                 AppleLegacyKeyExchange.Key.newBuilder()
                     .setKeyData(key.keyData)
                     .setRollingPeriod(144)
                     .setRollingStartNumber(key.rollingStartNumber)
-                    .setTransmissionRiskLevel(1)
+                    .setTransmissionRiskLevel(number)
                     .build()
             )
 
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml b/Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml
index 2aadb21fc..bd05b3126 100644
--- a/Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml
+++ b/Corona-Warn-App/src/main/res/layout/fragment_test_for_a_p_i.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
 
     <data>
 
@@ -111,11 +110,10 @@
                 android:lines="5"
                 android:visibility="gone" />
 
-            <ImageView
-                android:id="@+id/image_qr_code"
+            <androidx.viewpager2.widget.ViewPager2
+                android:id="@+id/qr_code_viewpager"
                 android:layout_width="match_parent"
-                android:layout_height="200dp"
-                app:srcCompat="@drawable/button" />
+                android:layout_height="200dp" />
 
             <TextView
                 android:id="@+id/label_other_keys"
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml b/Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml
index 64cadd014..c55150b3d 100644
--- a/Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml
+++ b/Corona-Warn-App/src/main/res/layout/fragment_test_risk_level_calculation.xml
@@ -49,6 +49,34 @@
                 app:showDetails="@{false}"
                 app:tracingViewModel="@{tracingViewModel}" />
 
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_normal"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Transmission Risk Level for scan: " />
+
+                <EditText
+                    android:id="@+id/transmission_number"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:ems="10"
+                    android:inputType="number"
+                    android:text="5" />
+            </LinearLayout>
+
+            <Button
+                android:id="@+id/button_provide_key_via_qr"
+                style="@style/buttonPrimary"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="@dimen/spacing_normal"
+                android:text="Scan Local QR Code" />
+
             <Button
                 android:id="@+id/button_retrieve_diagnosis_keys"
                 style="@style/buttonPrimary"
@@ -58,20 +86,20 @@
                 android:text="Retrieve Diagnosis Keys" />
 
             <Button
-                android:id="@+id/button_provide_key_via_qr"
+                android:id="@+id/button_calculate_risk_level"
                 style="@style/buttonPrimary"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Scan Local QR Code" />
+                android:text="Calculate Risk Level" />
 
             <Button
-                android:id="@+id/button_calculate_risk_level"
+                android:id="@+id/button_reset_risk_level"
                 style="@style/buttonPrimary"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Calculate Risk Level" />
+                android:text="Reset Risk Level" />
 
             <TextView
                 android:id="@+id/label_exposure_summary_title"
diff --git a/Corona-Warn-App/src/main/res/layout/test_qr_code_view.xml b/Corona-Warn-App/src/main/res/layout/test_qr_code_view.xml
new file mode 100644
index 000000000..c5ccf3344
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/test_qr_code_view.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/image_qr_code"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:srcCompat="@drawable/button" />
-- 
GitLab