Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Commit

Permalink
Merge pull request #280 from corona-warn-app/dev
Browse files Browse the repository at this point in the history
0.8.9
  • Loading branch information
jakobmoellerdev authored Jun 8, 2020
2 parents ef8e465 + 2540615 commit 9aada86
Show file tree
Hide file tree
Showing 52 changed files with 1,006 additions and 454 deletions.
4 changes: 2 additions & 2 deletions Corona-Warn-App/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ android {
applicationId 'de.rki.coronawarnapp'
minSdkVersion 23
targetSdkVersion 29
versionCode 15
versionName "0.8.8"
versionCode 16
versionName "0.8.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

buildConfigField "String", "DOWNLOAD_CDN_URL", "\"$DOWNLOAD_CDN_URL\""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import de.rki.coronawarnapp.exception.ApplicationConfigurationInvalidException
import de.rki.coronawarnapp.http.requests.RegistrationTokenRequest
import de.rki.coronawarnapp.http.requests.ReqistrationRequest
import de.rki.coronawarnapp.http.requests.TanRequestBody
import de.rki.coronawarnapp.http.service.DistributionService
import de.rki.coronawarnapp.http.service.SubmissionService
import de.rki.coronawarnapp.http.service.VerificationService
import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass.ApplicationConfiguration
import de.rki.coronawarnapp.service.diagnosiskey.DiagnosisKeyConstants
import de.rki.coronawarnapp.service.submission.SubmissionConstants
Expand All @@ -41,19 +44,36 @@ import java.io.File
import java.util.Date
import java.util.UUID

object WebRequestBuilder {
private val TAG: String? = WebRequestBuilder::class.simpleName

private const val EXPORT_BINARY_FILE_NAME = "export.bin"
private const val EXPORT_SIGNATURE_FILE_NAME = "export.sig"

private val serviceFactory = ServiceFactory()

private val distributionService by lazy { serviceFactory.distributionService() }
private val verificationService by lazy { serviceFactory.verificationService() }
private val submissionService by lazy { serviceFactory.submissionService() }
class WebRequestBuilder(
private val distributionService: DistributionService,
private val verificationService: VerificationService,
private val submissionService: SubmissionService,
private val verificationKeys: VerificationKeys
) {
companion object {
private val TAG: String? = WebRequestBuilder::class.simpleName
private const val EXPORT_BINARY_FILE_NAME = "export.bin"
private const val EXPORT_SIGNATURE_FILE_NAME = "export.sig"

@Volatile
private var instance: WebRequestBuilder? = null

fun getInstance(): WebRequestBuilder {
return instance ?: synchronized(this) {
instance ?: buildWebRequestBuilder().also { instance = it }
}
}

private val verificationKeys = VerificationKeys()
private fun buildWebRequestBuilder(): WebRequestBuilder {
val serviceFactory = ServiceFactory()
return WebRequestBuilder(
serviceFactory.distributionService(),
serviceFactory.verificationService(),
serviceFactory.submissionService(),
VerificationKeys()
)
}
}

suspend fun asyncGetDateIndex(): List<String> = withContext(Dispatchers.IO) {
return@withContext distributionService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ object NotificationHelper {
private fun buildNotification(title: String, content: String, visibility: Int): Notification? {
val builder = NotificationCompat.Builder(CoronaWarnApplication.getAppContext(), channelId)
.setSmallIcon(NotificationConstants.NOTIFICATION_SMALL_ICON)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(visibility)
.setContentIntent(createPendingIntentToMainActivity())
.setAutoCancel(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import de.rki.coronawarnapp.server.protocols.ApplicationConfigurationOuterClass

object ApplicationConfigurationService {
suspend fun asyncRetrieveApplicationConfiguration(): ApplicationConfigurationOuterClass.ApplicationConfiguration {
return WebRequestBuilder
.asyncGetApplicationConfigurationFromServer()
return WebRequestBuilder.getInstance().asyncGetApplicationConfigurationFromServer()
}

suspend fun asyncRetrieveExposureConfiguration(): ExposureConfiguration =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ object DiagnosisKeyService {
*/
suspend fun asyncSubmitKeys(authCode: String, keysToReport: List<KeyExportFormat.TemporaryExposureKey>) {
Log.d(TAG, "Diagnosis Keys will be submitted.")
WebRequestBuilder.asyncSubmitKeysToServer(
WebRequestBuilder.getInstance().asyncSubmitKeysToServer(
authCode,
false,
keysToReport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ object SubmissionService {

private suspend fun asyncRegisterDeviceViaGUID(guid: String) {
val registrationToken =
WebRequestBuilder.asyncGetRegistrationToken(
WebRequestBuilder.getInstance().asyncGetRegistrationToken(
guid,
QR_CODE_KEY_TYPE
)
Expand All @@ -35,7 +35,7 @@ object SubmissionService {

private suspend fun asyncRegisterDeviceViaTAN(tan: String) {
val registrationToken =
WebRequestBuilder.asyncGetRegistrationToken(
WebRequestBuilder.getInstance().asyncGetRegistrationToken(
tan,
TELE_TAN_KEY_TYPE
)
Expand All @@ -45,7 +45,7 @@ object SubmissionService {
}

suspend fun asyncRequestAuthCode(registrationToken: String): String {
return WebRequestBuilder.asyncGetTan(registrationToken)
return WebRequestBuilder.getInstance().asyncGetTan(registrationToken)
}

suspend fun asyncSubmitExposureKeys() {
Expand All @@ -58,7 +58,7 @@ object SubmissionService {
val registrationToken =
LocalData.registrationToken() ?: throw NoRegistrationTokenSetException()
return TestResult.fromInt(
WebRequestBuilder.asyncGetTestResult(registrationToken)
WebRequestBuilder.getInstance().asyncGetTestResult(registrationToken)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,28 @@ class MainFragment : BaseFragment() {
}

private fun setButtonOnClickListener() {
binding.mainTest.submissionStatusCardFetching.submissionStatusCardFetchingButton.setOnClickListener {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionResultFragment()
)
binding.mainTestUnregistered.submissionStatusCardUnregistered.setOnClickListener {
toSubmissionIntro()
}
binding.mainTest.submissionStatusCardContent.submissionStatusCardContentButton.setOnClickListener {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionResultFragment()
)
binding.mainTestUnregistered.submissionStatusCardUnregisteredButton.setOnClickListener {
toSubmissionIntro()
}
binding.mainTestPositive.submissionStatusCardPositiveResultShowButton.setOnClickListener {
binding.mainTestDone.submissionStatusCardDone.setOnClickListener {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionResultFragment()
MainFragmentDirections.actionMainFragmentToSubmissionDoneFragment()
)
}
binding.mainTest.submissionStatusCardUnregistered.submissionStatusCardUnregisteredButton.setOnClickListener {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionIntroFragment()
)
binding.mainTestResult.submissionStatusCardContent.setOnClickListener {
toSubmissionResult()
}
binding.mainTestResult.submissionStatusCardContentButton.setOnClickListener {
toSubmissionResult()
}
binding.mainTestPositive.submissionStatusCardPositive.setOnClickListener {
toSubmissionResult()
}
binding.mainTestPositive.submissionStatusCardPositiveButton.setOnClickListener {
toSubmissionResult()
}
binding.mainTracing.setOnClickListener {
doNavigate(MainFragmentDirections.actionMainFragmentToSettingsTracingFragment())
Expand All @@ -122,6 +125,18 @@ class MainFragment : BaseFragment() {
}
}

private fun toSubmissionResult() {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionResultFragment()
)
}

private fun toSubmissionIntro() {
doNavigate(
MainFragmentDirections.actionMainFragmentToSubmissionIntroFragment()
)
}

private fun showPopup(view: View) {
val popup = PopupMenu(requireContext(), view)
popup.inflate(R.menu.menu_main)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {

Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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')
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {

Expand All @@ -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

Expand All @@ -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() }
Expand All @@ -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()
}

Expand All @@ -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() ?: ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ object CachedKeyFileHolder {
return@withContext getLast3Hours(currentDate)
.map { getURLForHour(currentDate.toServerFormat(), it) }
.map { url -> async {
return@async WebRequestBuilder.asyncGetKeyFilesFromServer(url)
return@async WebRequestBuilder.getInstance().asyncGetKeyFilesFromServer(url)
} }.awaitAll()
} else {
throw IllegalStateException(
Expand Down Expand Up @@ -152,7 +152,7 @@ object CachedKeyFileHolder {
*/
private suspend fun String.createDayEntryForUrl() = keyCache.createEntry(
this.generateCacheKeyFromString(),
WebRequestBuilder.asyncGetKeyFilesFromServer(this).toURI(),
WebRequestBuilder.getInstance().asyncGetKeyFilesFromServer(this).toURI(),
DAY
)

Expand Down Expand Up @@ -183,13 +183,13 @@ object CachedKeyFileHolder {
* Get all dates from server based as formatted dates
*/
private suspend fun getDatesFromServer() =
WebRequestBuilder.asyncGetDateIndex()
WebRequestBuilder.getInstance().asyncGetDateIndex()

/**
* Get all hours from server based as formatted dates
*/
private suspend fun getHoursFromServer(day: Date) =
WebRequestBuilder.asyncGetHourIndex(day)
WebRequestBuilder.getInstance().asyncGetHourIndex(day)

/**
* TODO remove before release
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading

0 comments on commit 9aada86

Please sign in to comment.