diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt index b0818144..707bbca3 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/EnterTanDialog.kt @@ -26,7 +26,6 @@ import net.dankito.banking.ui.model.TanMediumStatus import net.dankito.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium import net.dankito.fints.model.* import net.dankito.fints.response.client.FinTsClientResponse -import net.dankito.fints.tan.TanImage open class EnterTanDialog : DialogFragment() { @@ -136,7 +135,14 @@ open class EnterTanDialog : DialogFragment() { if (tanChallenge is FlickercodeTanChallenge) { val flickerCodeView = rootView.flickerCodeView flickerCodeView.visibility = View.VISIBLE - flickerCodeView.setCode((tanChallenge as FlickercodeTanChallenge).flickercode) + + val flickercode = (tanChallenge as FlickercodeTanChallenge).flickercode + if (flickercode.decodingSuccessful) { + flickerCodeView.setCode(flickercode) + } + else { + showDecodingTanChallengeFailedErrorDelayed(flickercode.error) + } } else if (tanChallenge is ImageTanChallenge) { rootView.tanImageView.visibility = View.VISIBLE @@ -147,7 +153,7 @@ open class EnterTanDialog : DialogFragment() { rootView.imgTanImageView.setImageBitmap(bitmap) } else { - showDecodingTanImageFailedErrorDelayed(decodedImage) // this method gets called right on start up before dialog is shown -> Alert would get displayed before dialog and therefore covered by dialog if we don't delay displaying dialog + showDecodingTanChallengeFailedErrorDelayed(decodedImage.error) } } @@ -156,16 +162,20 @@ open class EnterTanDialog : DialogFragment() { } - protected open fun showDecodingTanImageFailedErrorDelayed(decodedImage: TanImage) { + /** + * This method gets called right on start up before dialog is shown -> Alert would get displayed before dialog and + * therefore covered by dialog -> delay displaying alert. + */ + protected open fun showDecodingTanChallengeFailedErrorDelayed(error: Exception?) { val handler = Handler() - handler.postDelayed({ showDecodingTanImageFailedError(decodedImage) }, 500) + handler.postDelayed({ showDecodingTanChallengeFailedError(error) }, 500) } - protected open fun showDecodingTanImageFailedError(decodedImage: TanImage) { + protected open fun showDecodingTanChallengeFailedError(error: Exception?) { activity?.let { context -> AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_enter_tan_error_could_not_decode_tan_image, decodedImage.error?.localizedMessage)) + .setMessage(context.getString(R.string.dialog_enter_tan_error_could_not_decode_tan_image, error?.localizedMessage)) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } .show() } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/Flickercode.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/Flickercode.kt index 9b1aec90..ac245a9e 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/Flickercode.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/Flickercode.kt @@ -3,5 +3,20 @@ package net.dankito.fints.tan open class Flickercode( val challengeHHD_UC: String, - val parsedDataSet: String -) \ No newline at end of file + val parsedDataSet: String, + val error: Exception? = null +) { + + val decodingSuccessful: Boolean + get() = error == null + + + override fun toString(): String { + if (decodingSuccessful == false) { + return "Decoding error: $error" + } + + return "Parsed $challengeHHD_UC to $parsedDataSet" + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/FlickercodeDecoder.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/FlickercodeDecoder.kt index 7c655b28..39a87477 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/FlickercodeDecoder.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/FlickercodeDecoder.kt @@ -1,5 +1,6 @@ package net.dankito.fints.tan +import org.slf4j.LoggerFactory import java.util.regex.Pattern @@ -7,36 +8,44 @@ open class FlickercodeDecoder { companion object { val ContainsOtherSymbolsThanFiguresPattern: Pattern = Pattern.compile("\\D") + + private val log = LoggerFactory.getLogger(FlickercodeDecoder::class.java) } open fun decodeChallenge(challengeHHD_UC: String): Flickercode { - val challengeLength = parseIntToHex(challengeHHD_UC.substring(0, 2)) + try { + val challengeLength = parseIntToHex(challengeHHD_UC.substring(0, 2)) - val startCode = parseStartCode(challengeHHD_UC, 2) + val startCode = parseStartCode(challengeHHD_UC, 2) - val controlByte = "" // TODO (there can be multiple of them!) + val controlByte = "" // TODO (there can be multiple of them!) - val de1 = parseDatenelement(challengeHHD_UC, startCode.endIndex) - val de2 = parseDatenelement(challengeHHD_UC, de1.endIndex) - val de3 = parseDatenelement(challengeHHD_UC, de2.endIndex) + val de1 = parseDatenelement(challengeHHD_UC, startCode.endIndex) + val de2 = parseDatenelement(challengeHHD_UC, de1.endIndex) + val de3 = parseDatenelement(challengeHHD_UC, de2.endIndex) - val luhnChecksum = calculateLuhnChecksum(startCode, controlByte, de1, de2, de3) + val luhnChecksum = calculateLuhnChecksum(startCode, controlByte, de1, de2, de3) - // TODO: - // können im HHDUC-Protokoll Datenelemente ausgelassen werden, indem als Länge LDE1, LDE2 oder LDE3 = ‘00‘ angegeben wird. - // Dadurch wird gekennzeichnet, dass das jeweilige, durch den Start-Code definierte Datenelement nicht im HHDUC-Datenstrom - // enthalten ist. Somit sind für leere Datenelemente die Längenfelder zu übertragen, wenn danach noch nicht-leere - // Datenelemente folgen. Leere Datenelemente am Ende des Datenstromes können komplett inklusive Längenfeld entfallen. - val dataWithoutLengthAndChecksum = startCode.lengthInByte + controlByte + startCode + de1.lengthInByte + de1.data + de2.lengthInByte + de2.data + de3.lengthInByte + de3.data - val dataLength = (dataWithoutLengthAndChecksum.length + 2) / 2 // + 2 for checksum - val dataWithoutChecksum = toHex(dataLength, 2) + dataWithoutLengthAndChecksum + // TODO: + // können im HHDUC-Protokoll Datenelemente ausgelassen werden, indem als Länge LDE1, LDE2 oder LDE3 = ‘00‘ angegeben wird. + // Dadurch wird gekennzeichnet, dass das jeweilige, durch den Start-Code definierte Datenelement nicht im HHDUC-Datenstrom + // enthalten ist. Somit sind für leere Datenelemente die Längenfelder zu übertragen, wenn danach noch nicht-leere + // Datenelemente folgen. Leere Datenelemente am Ende des Datenstromes können komplett inklusive Längenfeld entfallen. + val dataWithoutLengthAndChecksum = startCode.lengthInByte + controlByte + startCode + de1.lengthInByte + de1.data + de2.lengthInByte + de2.data + de3.lengthInByte + de3.data + val dataLength = (dataWithoutLengthAndChecksum.length + 2) / 2 // + 2 for checksum + val dataWithoutChecksum = toHex(dataLength, 2) + dataWithoutLengthAndChecksum - val xorChecksumString = calculateXorChecksum(dataWithoutChecksum) + val xorChecksumString = calculateXorChecksum(dataWithoutChecksum) - val parsedDataSet = dataWithoutChecksum + luhnChecksum + xorChecksumString + val parsedDataSet = dataWithoutChecksum + luhnChecksum + xorChecksumString - return Flickercode(challengeHHD_UC, parsedDataSet) + return Flickercode(challengeHHD_UC, parsedDataSet) + } catch (e: Exception) { + log.error("Could not decode challenge $challengeHHD_UC") + + return Flickercode(challengeHHD_UC, "", e) + } } protected fun parseStartCode(challengeHHD_UC: String, startIndex: Int): FlickercodeDatenelement { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt index 83a5f827..14aeb818 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt @@ -12,6 +12,10 @@ open class TanImage( override fun toString(): String { + if (decodingSuccessful == false) { + return "Decoding error: $error" + } + return "$mimeType ${imageBytes.size} bytes" }