diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModel.kt index c18d49a015360e7b4747526b6a323afe69dd88cc..cae07743b57a1c5431a917d6a582d03244178564 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel import de.rki.coronawarnapp.storage.SubmissionRepository +import de.rki.coronawarnapp.util.TanHelper class SubmissionTanViewModel : ViewModel() { @@ -16,7 +17,21 @@ class SubmissionTanViewModel : ViewModel() { val isValidTanFormat = Transformations.map(tan) { - it != null && it.length == TanConstants.MAX_LENGTH + it != null && + it.length == TanConstants.MAX_LENGTH && + TanHelper.isChecksumValid(it) && + TanHelper.allCharactersValid(it) + } + + val tanChecksumValid = + Transformations.map(tan) { + ((it !== null && it.trim().length == TanConstants.MAX_LENGTH) && + TanHelper.isChecksumValid(it).not()).not() + } + + val tanCharactersValid = + Transformations.map(tan) { + !((it != null) && TanHelper.allCharactersValid(it).not()) } fun storeTeletan() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt index b6be13be81def6ec21f396f0b89f278cdeb9fd6a..644cf1ee0f3aca6bb52e87b0fcca899b24065220 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt @@ -1,6 +1,6 @@ package de.rki.coronawarnapp.ui.submission object TanConstants { - const val MAX_LENGTH = 7 + const val MAX_LENGTH = 10 val ALPHA_NUMERIC_CHARS = ('a'..'z').plus('A'..'Z').plus('0'..'9') } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanInput.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanInput.kt index 70cc48b6833364f5303cc7d9d7549f2962e58e7f..b7b02a82572a39e6528fb9ab183137028892fae2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanInput.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanInput.kt @@ -8,6 +8,9 @@ import android.view.inputmethod.InputMethodManager import android.widget.FrameLayout import androidx.core.widget.doOnTextChanged import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.TanConstants.MAX_LENGTH +import de.rki.coronawarnapp.util.TanHelper +import java.util.Locale import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_edittext import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_1 import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_2 @@ -16,6 +19,11 @@ import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_4 import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_5 import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_6 import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_7 +import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_8 +import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_9 +import kotlinx.android.synthetic.main.view_tan_input.view.tan_input_textview_10 +import kotlinx.android.synthetic.main.view_tan_input.view.dash_view_1 +import kotlinx.android.synthetic.main.view_tan_input.view.dash_view_2 class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) { @@ -30,7 +38,7 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att TanConstants.ALPHA_NUMERIC_CHARS.contains(it) } } - private val lengthFilter = InputFilter.LengthFilter(TanConstants.MAX_LENGTH) + private var lengthFilter = InputFilter.LengthFilter(MAX_LENGTH) var listener: ((String?) -> Unit)? = null @@ -41,6 +49,9 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att tan_input_edittext.filters = arrayOf(whitespaceFilter, alphaNumericFilter, lengthFilter) + dash_view_1.text = "-" + dash_view_2.text = "-" + // register listener tan_input_edittext.doOnTextChanged { text, _, _, _ -> updateTan(text) } setOnClickListener { showKeyboard() } @@ -56,9 +67,20 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att } } + private fun limitLength(length: Int?) { + lengthFilter = InputFilter.LengthFilter(if (length != null) length else MAX_LENGTH) + tan_input_edittext.filters = arrayOf(whitespaceFilter, alphaNumericFilter, lengthFilter) + } + private fun updateTan(text: CharSequence?) { - this.tan = text?.toString() + this.tan = text?.toString()?.toUpperCase(Locale.ROOT) updateDigits() + tan?.let { + limitLength( + if (TanHelper.allCharactersValid(it)) null + else it.length + ) + } notifyListener() } @@ -71,9 +93,24 @@ class TanInput(context: Context, attrs: AttributeSet) : FrameLayout(context, att tan_input_textview_4, tan_input_textview_5, tan_input_textview_6, - tan_input_textview_7 + tan_input_textview_7, + tan_input_textview_8, + tan_input_textview_9, + tan_input_textview_10 ).forEachIndexed { i, tanDigit -> tanDigit.text = digitAtIndex(i) + tanDigit.background = + if (digitAtIndex(i) == "") + resources.getDrawable(R.drawable.tan_input_digit, null) + else if (TanHelper.isTanCharacterValid(digitAtIndex(i))) + resources.getDrawable(R.drawable.tan_input_digit_entered, null) + else resources.getDrawable(R.drawable.tan_input_digit_error, null) + + tanDigit.setTextColor( + if (TanHelper.isTanCharacterValid(digitAtIndex(i))) + resources.getColor(R.color.colorTextSemanticNeutral, null) + else resources.getColor(R.color.colorTextSemanticRed, null) + ) } private fun digitAtIndex(index: Int): String = tan?.getOrNull(index)?.toString() ?: "" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..7fa1562c70e11f15ba92758f2e813d96a15127a4 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt @@ -0,0 +1,35 @@ +package de.rki.coronawarnapp.util + +import de.rki.coronawarnapp.ui.submission.TanConstants.MAX_LENGTH +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.util.Locale + +object TanHelper { + private const val VALID_CHARACTERS = "23456789ABCDEFGHJKMNPQRSTUVWXYZ" + + fun isChecksumValid(tan: String): Boolean { + if (tan.trim().length != MAX_LENGTH) + return false + val subTan = tan.substring(0, MAX_LENGTH - 1).toUpperCase(Locale.ROOT) + val tanDigest = MessageDigest.getInstance("SHA-256") + .digest(subTan.toByteArray(StandardCharsets.US_ASCII)) + var checkChar = "%02x".format(tanDigest[0])[0] + if (checkChar == '0') checkChar = 'G' + if (checkChar == '1') checkChar = 'H' + + return checkChar.toUpperCase() == tan.last().toUpperCase() + } + + fun allCharactersValid(tan: String): Boolean { + for (character in tan) { + if (!isTanCharacterValid(character.toString())) + return false + } + return true + } + + fun isTanCharacterValid(character: String): Boolean { + return VALID_CHARACTERS.contains(character) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt index 22f1b6b6acdbbc58c3f595e740e6d805a99e791e..7f16f3ed7ff0c62f1254e8213738e1b539fb6da7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt @@ -193,3 +193,8 @@ fun formatShowRiskStatusCard(deviceUiState: DeviceUIState?): Int = deviceUiState != DeviceUIState.PAIRED_POSITIVE_TELETAN && deviceUiState != DeviceUIState.SUBMITTED_FINAL ) + +fun formatShowTanCharacterError( + charactersValid: Boolean, + checksumValid: Boolean +): Int = formatVisibility(checksumValid && !charactersValid) diff --git a/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml b/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml index d06cdb9200e0e17be9a2d49cbc16c86faf412081..48e3d0c51a82c6484dfaad67960e18c39a9fa341 100644 --- a/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml +++ b/Corona-Warn-App/src/main/res/drawable/tan_input_digit.xml @@ -1,11 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <corners android:radius="@dimen/submission_tan_input_digit_radius" /> - <gradient - android:angle="90" - android:centerColor="@color/colorSurface2" - android:centerX="0.02" - android:endColor="@color/colorSurface2" - android:startColor="@color/colorTextSemanticNeutral" /> -</shape> \ No newline at end of file +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> +<item> + <shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorSurface2" /> + </shape> +</item> +<item + android:height="1.5dp" + android:gravity="bottom"> + <shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> + <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorTextSemanticNeutral" /> + </shape> +</item> +</layer-list> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/tan_input_digit_entered.xml b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_entered.xml new file mode 100644 index 0000000000000000000000000000000000000000..0550164c034b36b235de59a69df0cf69b5088316 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_entered.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorSurface2" /> +</shape> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml new file mode 100644 index 0000000000000000000000000000000000000000..01780eeb09b0b18076cb5bd414e1a2274c3d4015 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/tan_input_digit_error.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorSurface2" /> + </shape> + </item> + <item + android:height="1.5dp" + android:gravity="bottom"> + <shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:bottomLeftRadius="@dimen/submission_tan_input_digit_radius" /> + <corners android:bottomRightRadius="@dimen/submission_tan_input_digit_radius" /> + <solid android:color="@color/colorTextSemanticRed" /> + </shape> + </item> +</layer-list> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml index c940d7044756bbc18ba88d9b6393936436795151..4337a0b5e99b2d44ccadf2aea29579f74a14fcf1 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml @@ -5,6 +5,7 @@ <data> + <import type="de.rki.coronawarnapp.util.formatter.FormatterHelper" /> <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> <variable @@ -54,6 +55,33 @@ app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_tan_body" /> + <TextView + android:id="@+id/submission_tan_character_error" + style="@style/subtitle" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterSubmissionHelper.formatShowTanCharacterError(viewmodel.tanCharactersValid, viewmodel.tanChecksumValid)}" + android:text="@string/submission_tan_character_error" + android:textColor="@color/colorTextSemanticRed" + app:layout_constraintEnd_toStartOf="@+id/guideline_end" + app:layout_constraintStart_toStartOf="@+id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_tan_input" /> + + <TextView + android:id="@+id/submission_tan_error" + style="@style/subtitle" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterHelper.formatVisibility(!viewmodel.tanChecksumValid)}" + android:text="@string/submission_tan_error" + android:textColor="@color/colorTextSemanticRed" + app:layout_constraintEnd_toStartOf="@+id/guideline_end" + app:layout_constraintStart_toStartOf="@+id/guideline_start" + app:layout_constraintTop_toBottomOf="@id/submission_tan_input" /> + + <Button android:id="@+id/submission_tan_button_enter" style="@style/buttonPrimary" diff --git a/Corona-Warn-App/src/main/res/layout/view_tan_input.xml b/Corona-Warn-App/src/main/res/layout/view_tan_input.xml index 213e9ff71c18e439291b5c0443c14ff26fff0ae2..9083b562845012532d3bc66852b9b20a93ee1ffa 100644 --- a/Corona-Warn-App/src/main/res/layout/view_tan_input.xml +++ b/Corona-Warn-App/src/main/res/layout/view_tan_input.xml @@ -36,17 +36,26 @@ android:id="@+id/tan_input_textview_3" style="@style/tanInputDigit" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_4" + app:layout_constraintEnd_toStartOf="@+id/dash_view_1" app:layout_constraintStart_toEndOf="@+id/tan_input_textview_2" app:layout_constraintTop_toTopOf="parent" tools:text="X" /> + <TextView + android:id="@+id/dash_view_1" + style="@style/tanInputDash" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_4" + app:layout_constraintStart_toEndOf="@+id/tan_input_textview_3" + app:layout_constraintTop_toTopOf="parent" + tools:text="-" /> + <TextView android:id="@+id/tan_input_textview_4" style="@style/tanInputDigit" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_5" - app:layout_constraintStart_toEndOf="@+id/tan_input_textview_3" + app:layout_constraintStart_toEndOf="@+id/dash_view_1" app:layout_constraintTop_toTopOf="parent" tools:text="X" /> @@ -63,19 +72,54 @@ android:id="@+id/tan_input_textview_6" style="@style/tanInputDigit" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_7" + app:layout_constraintEnd_toStartOf="@+id/dash_view_2" app:layout_constraintStart_toEndOf="@+id/tan_input_textview_5" app:layout_constraintTop_toTopOf="parent" tools:text="X" /> + <TextView + android:id="@+id/dash_view_2" + style="@style/tanInputDash" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_7" + app:layout_constraintStart_toEndOf="@+id/tan_input_textview_6" + app:layout_constraintTop_toTopOf="parent" + tools:text="-" /> + <TextView android:id="@+id/tan_input_textview_7" style="@style/tanInputDigit" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@+id/tan_input_textview_6" + app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_8" + app:layout_constraintStart_toEndOf="@+id/dash_view_2" app:layout_constraintTop_toTopOf="parent" tools:text="X" /> + <TextView + android:id="@+id/tan_input_textview_8" + style="@style/tanInputDigit" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_9" + app:layout_constraintStart_toEndOf="@+id/tan_input_textview_7" + app:layout_constraintTop_toTopOf="parent" + tools:text="X" /> + + <TextView + android:id="@+id/tan_input_textview_9" + style="@style/tanInputDigit" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintEnd_toStartOf="@+id/tan_input_textview_10" + app:layout_constraintStart_toEndOf="@+id/tan_input_textview_8" + app:layout_constraintTop_toTopOf="parent" + tools:text="X" /> + + <TextView + android:id="@+id/tan_input_textview_10" + style="@style/tanInputDigit" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/tan_input_textview_9" + app:layout_constraintTop_toTopOf="parent" + tools:text="X" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index bb5abba0bac59de40e16bc170991df0dff7e7080..9555649d9518546120c31b7c82d5c0d0e3a8ed2a 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -591,7 +591,7 @@ <!-- XHED: Page title for TAN submission pge --> <string name="submission_tan_title">"TAN-Eingabe"</string> <!-- YTXT: Body text for the tan submission page --> - <string name="submission_tan_body">"Die TAN ist 7-stellig und Groß- und Kleinschreibung muss nicht beachtet werden.\n\nGeben Sie bitte die Ihnen mitgeteilte TAN ein:"</string> + <string name="submission_tan_body">"Geben Sie bitte die 10 Stellen der TAN ein, die Ihnen mitgeteilt wurde."</string> <!-- XBUT: Submit TAN button --> <string name="submission_tan_button_text">"Weiter"</string> diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index a5228799382cf1ae7aa638135618d68f7920aa2f..139deb4061106861b72cf1bf9181212a220a075f 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -593,7 +593,7 @@ <!-- XHED: Page title for TAN submission pge --> <string name="submission_tan_title">"TAN entry"</string> <!-- YTXT: Body text for the tan submission page --> - <string name="submission_tan_body">"The TAN has 7 characters and is case-insensitive.\n\nPlease enter the TAN you were given:"</string> + <string name="submission_tan_body">"Please enter the 10 characters of the TAN you were given:"</string> <!-- XBUT: Submit TAN button --> <string name="submission_tan_button_text">"Next"</string> diff --git a/Corona-Warn-App/src/main/res/values/dimens.xml b/Corona-Warn-App/src/main/res/values/dimens.xml index e23305dc4a2f0a107bc2366ae574c34a2013852d..5b3c0277c6fbe5197046ffa0f2750da31c16a28f 100644 --- a/Corona-Warn-App/src/main/res/values/dimens.xml +++ b/Corona-Warn-App/src/main/res/values/dimens.xml @@ -82,8 +82,10 @@ <!-- Submission Tan Input --> <dimen name="submission_tan_input_edittext_size">1dp</dimen> <dimen name="submission_tan_input_digit_radius">2dp</dimen> - <dimen name="submission_tan_input_digit_width">24dp</dimen> - <dimen name="submission_tan_input_digit_height">32dp</dimen> + <dimen name="submission_tan_input_digit_width">22dp</dimen> + <dimen name="submission_tan_input_digit_height">40dp</dimen> + <dimen name="submission_tan_input_dash_width">11dp</dimen> + <dimen name="submission_tan_input_dash_height">40dp</dimen> <!-- Submission QR Code Scan --> <dimen name="submission_scan_qr_code_viewfinder_size">240dp</dimen> diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 0b6468412b1cf6bf5089c1a58d445c7fef7dd09c..ead09a21a67e7e23db4be012b3d41b13b9d862fa 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -2664,9 +2664,13 @@ as modifying the License. <!-- XHED: Page title for TAN submission pge --> <string name="submission_tan_title">TAN Eingabe</string> <!-- YTXT: Body text for the tan submission page --> - <string name="submission_tan_body">Die TAN ist 7-stellig und Groß- und Kleinschreibung muss beachtet werden.\n\nGeben Sie bitte die Ihnen mitgeteilte TAN ein:</string> + <string name="submission_tan_body">Geben Sie bitte die 10 Stellen der TAN ein, die Ihnen mitgeteilt wurde.</string> <!-- XBUT: Submit TAN button --> <string name="submission_tan_button_text">Weiter</string> + <!-- YTXT: Error text for the tan submission page --> + <string name="submission_tan_error">Ungültige TAN, bitte überprüfen Sie Ihre Eingabe.</string> + <!-- YTXT: Error text for the tan submission page (wrong characters) --> + <string name="submission_tan_character_error">Ungültige Eingabe, bitte überprüfen Sie das Zeichen.</string> <!-- Submission Intro --> <!-- XHED: Page title for menu at the start of the submission process --> diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index 8ea177f88f4483131faf5b067e9257817e27c5fd..4039163558da94eb9bb6de99666ac759738af23f 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -219,6 +219,13 @@ <item name="android:gravity">center</item> </style> + <style name="tanInputDash" parent="headline6"> + <item name="android:layout_width">@dimen/submission_tan_input_dash_width</item> + <item name="android:layout_height">@dimen/submission_tan_input_dash_height</item> + <item name="android:background">@null</item> + <item name="android:gravity">center</item> + </style> + <style name="tanInputEdittext"> <item name="android:layout_width">@dimen/submission_tan_input_edittext_size</item> <item name="android:layout_height">@dimen/submission_tan_input_edittext_size</item> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe08e5de2a9d974e1c13b4d11111cee9b0cd84d2 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt @@ -0,0 +1,61 @@ +package de.rki.coronawarnapp.ui.submission + +import com.google.android.gms.nearby.exposurenotification.ExposureSummary +import de.rki.coronawarnapp.storage.SubmissionRepository +import de.rki.coronawarnapp.util.TanHelper +import io.mockk.* +import org.junit.Assert.* +import org.junit.Test + +class SubmissionTanViewModelTest { + private var viewModel: SubmissionTanViewModel = SubmissionTanViewModel() + + @Test + fun allCharactersValid() { + viewModel.tan.postValue("ABCD") + viewModel.tanCharactersValid.value?.let { assertTrue(it) } + + viewModel.tan.postValue("ABCD0") + viewModel.tanCharactersValid.value?.let { assertFalse(it) } + + } + + @Test + fun tanFormatValid() { + viewModel.tan.postValue("ZWFPC7NG47") + viewModel.isValidTanFormat.value?.let { assertTrue(it) } + + viewModel.tan.postValue("ABC") + viewModel.isValidTanFormat.value?.let { assertFalse(it) } + + viewModel.tan.postValue("ZWFPC7NG48") + viewModel.isValidTanFormat.value?.let { assertFalse(it) } + + viewModel.tan.postValue("ZWFPC7NG4A") + viewModel.isValidTanFormat.value?.let { assertFalse(it) } + } + + @Test + fun checksumValid() { + viewModel.tan.postValue("ZWFPC7NG47") + viewModel.tanChecksumValid.value?.let { assertTrue(it) } + + viewModel.tan.postValue("ZWFPC7NG48") + viewModel.tanChecksumValid.value?.let { assertFalse(it) } + } + + @Test + fun testTanStorage() { + val sr = mockk<SubmissionRepository> { + every { setTeletan(any()) } just Runs + } + val tan = "ZWFPC7NG47"; + sr.setTeletan(tan) + + verify (exactly = 1) { sr.setTeletan( + withArg { + assertEquals(it, tan) + }) + } + } +} \ No newline at end of file diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..d3c4a11201a9d5db9e242e35ff16964a7ba130ce --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt @@ -0,0 +1,88 @@ +package de.rki.coronawarnapp.util + +import de.rki.coronawarnapp.service.submission.SubmissionService +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert +import org.junit.Test + +class TanHelperTest { + + @Test + fun isValidCharacter() { + // valid + val validCharacters = arrayOf( + "2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H", + "J","K","M","N","P","Q","R","S","T","U","V","W","X","Y","Z" + ) + for (character in validCharacters) { + MatcherAssert.assertThat( + TanHelper.isTanCharacterValid(character), + CoreMatchers.equalTo(true) + ) + } + + // invalid + val invalidCharacters = arrayOf( + "0","1","O","L","I","Ö","*","&","-","a","b", + "c","ö","ß","é","."," ","€","(",")",";","," + ) + for (character in invalidCharacters) { + MatcherAssert.assertThat( + TanHelper.isTanCharacterValid(character), + CoreMatchers.equalTo(false) + ) + } + } + + @Test + fun areCharactersValid() { + // valid input strings (not necessarily valid TANs) + val validStrings = arrayOf( + "ABCD", "2345", "PTPHM35RP4", "AAAAAAAAAA", "BBBBB") + for (text in validStrings) { + MatcherAssert.assertThat( + TanHelper.allCharactersValid(text), + CoreMatchers.equalTo(true) + ) + } + + // invalid input strings + val invalidStrings = arrayOf( + "ABCDÖ", "01234", "PTPHM15RP4", "AAAAAA AAA", "BB.BBB") + for (text in invalidStrings) { + MatcherAssert.assertThat( + TanHelper.allCharactersValid(text), + CoreMatchers.equalTo(false) + ) + } + } + + @Test + fun isChecksumValid() { + // valid + val validTans = arrayOf( + "9A3B578UMG", "DEU7TKSV3H", "PTPHM35RP4", "V923D59AT8", "H9NC5CQ34E") + for (tan in validTans) { + MatcherAssert.assertThat( + TanHelper.isChecksumValid(tan), + CoreMatchers.equalTo(true) + ) + } + + // invalid + val invalidTans = arrayOf( + "DEU7TKSV32", "DEU7TKSV33", "DEU7TKSV34", "DEU7TKSV35", + "DEU7TKSV36", "DEU7TKSV37", "DEU7TKSV38", "DEU7TKSV39", + "DEU7TKSV3A", "DEU7TKSV3B", "DEU7TKSV3C", "DEU7TKSV3D", + "DEU7TKSV3E", "DEU7TKSV3F", "DEU7TKSV3G", + " QV5FQ38MA", + "9A3B578UM0", "DEU7TKSV31", "Q4XBJCB43", "929B96CA8" + ) + for (tan in invalidTans) { + MatcherAssert.assertThat( + TanHelper.isChecksumValid(tan), + CoreMatchers.equalTo(false) + ) + } + } +} \ No newline at end of file