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 f845e212..cee01f00 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
@@ -1,6 +1,7 @@
package net.dankito.banking.fints4java.android.ui.dialogs
import android.content.Context
+import android.graphics.BitmapFactory
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
@@ -11,6 +12,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Spinner
import kotlinx.android.synthetic.main.dialog_enter_tan.view.*
+import kotlinx.android.synthetic.main.view_tan_image.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
@@ -26,11 +28,14 @@ import net.dankito.fints.model.TanChallenge
import net.dankito.fints.model.TanProcedureType
import net.dankito.fints.response.client.FinTsClientResponse
import net.dankito.fints.tan.FlickercodeDecoder
+import net.dankito.fints.tan.TanImageDecoder
open class EnterTanDialog : DialogFragment() {
companion object {
+ val OpticalTanProcedures = listOf(TanProcedureType.ChipTanOptisch, TanProcedureType.ChipTanQrCode, TanProcedureType.PhotoTan)
+
const val DialogTag = "EnterTanDialog"
}
@@ -70,17 +75,28 @@ open class EnterTanDialog : DialogFragment() {
}
protected open fun setupUI(rootView: View) {
- val flickerCodeView = rootView.flickerCodeView
-
setupSelectTanProcedureView(rootView)
- if (tanChallenge.tanProcedure.type == TanProcedureType.ChipTanOptisch) {
+ if (OpticalTanProcedures.contains(tanChallenge.tanProcedure.type)) {
if (account.tanMedia.isNotEmpty()) {
setupSelectTanMediumView(rootView)
}
- flickerCodeView.visibility = View.VISIBLE
- flickerCodeView.setCode(FlickercodeDecoder().decodeChallenge(tanChallenge.tanChallenge))
+ if (tanChallenge.tanProcedure.type == TanProcedureType.ChipTanOptisch) {
+ val flickerCodeView = rootView.flickerCodeView
+ flickerCodeView.visibility = View.VISIBLE
+ flickerCodeView.setCode(FlickercodeDecoder().decodeChallenge(tanChallenge.tanChallenge))
+ }
+ else if (tanChallenge.tanProcedure.type == TanProcedureType.ChipTanQrCode
+ || tanChallenge.tanProcedure.type == TanProcedureType.PhotoTan) {
+ rootView.tanImageView.visibility = View.VISIBLE
+
+ TanImageDecoder().decodeChallenge(tanChallenge.tanChallenge)?.let { tanImage ->
+ val bitmap = BitmapFactory.decodeByteArray(tanImage.imageBytes, 0, tanImage.imageBytes.size)
+ rootView.imgTanImageView.setImageBitmap(bitmap)
+ }
+ // TODO: what to do if decoding fails? At least a message should get shown to user
+ }
rootView.edtxtEnteredTan.inputType = InputType.TYPE_CLASS_NUMBER
}
diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt
index 09f81ec7..ca79fb81 100644
--- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt
+++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/ChipTanFlickerCodeView.kt
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_flicker_code.view.*
+import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.fints4java.android.util.FlickercodeAnimator
import net.dankito.fints.tan.Bit
diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageSizeControlsView.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageSizeControlsView.kt
new file mode 100644
index 00000000..ddcfc5bd
--- /dev/null
+++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageSizeControlsView.kt
@@ -0,0 +1,24 @@
+package net.dankito.banking.fints4java.android.ui.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import net.dankito.banking.fints4java.android.R
+
+
+open class TanImageSizeControlsView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+
+ init {
+ setupUi(context)
+ }
+
+ private fun setupUi(context: Context) {
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ inflater.inflate(R.layout.view_tan_image_size_controls, this, true)
+ }
+
+}
\ No newline at end of file
diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageView.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageView.kt
new file mode 100644
index 00000000..390bb021
--- /dev/null
+++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/views/TanImageView.kt
@@ -0,0 +1,80 @@
+package net.dankito.banking.fints4java.android.ui.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.LinearLayout
+import kotlinx.android.synthetic.main.view_tan_image.view.*
+import kotlinx.android.synthetic.main.view_tan_image_size_controls.view.*
+import net.dankito.banking.fints4java.android.R
+
+
+open class TanImageView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+ companion object {
+ const val ChangeSizeStepSizeDp = 10f
+ }
+
+
+ protected val MinHeight: Int
+
+ protected val MaxHeight: Int
+
+ protected lateinit var imgTanImageView: ImageView
+
+
+ init {
+ MinHeight = convertDpToPx(context, 150f).toInt()
+ MaxHeight = convertDpToPx(context, 500f).toInt()
+
+ setupUi(context)
+ }
+
+ private fun setupUi(context: Context) {
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ val rootView = inflater.inflate(R.layout.view_tan_image, this, true)
+
+ imgTanImageView = rootView.imgTanImageView
+
+ rootView.btnIncreaseSize.setOnClickListener { increaseSize() }
+ rootView.btnDecreaseSize.setOnClickListener { decreaseSize() }
+ }
+
+
+ open fun increaseSize() {
+ changeSizeBy(ChangeSizeStepSizeDp)
+ }
+
+ open fun decreaseSize() {
+ changeSizeBy(ChangeSizeStepSizeDp * -1)
+ }
+
+ protected open fun changeSizeBy(changeSizeBy: Float) {
+ val params = imgTanImageView.layoutParams
+ val newWidthAndHeight = params.height + convertDpToPx(context, changeSizeBy).toInt()
+
+ if (newWidthAndHeight in MinHeight..MaxHeight) {
+ params.height = newWidthAndHeight
+ params.width = newWidthAndHeight
+
+ imgTanImageView.layoutParams = params // TODO: needed?
+
+ requestLayout()
+ }
+ }
+
+ /**
+ * This method converts dp unit to equivalent pixels, depending on device density.
+ *
+ * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
+ * @param context Context to get resources and device specific display metrics
+ * @return A float value to represent px equivalent to dp depending on device density
+ */
+ open fun convertDpToPx(context: Context, dp: Float): Float {
+ return dp * context.resources.displayMetrics.density
+ }
+
+}
\ No newline at end of file
diff --git a/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml b/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml
index 234657e5..459200be 100644
--- a/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml
+++ b/fints4javaAndroidApp/src/main/res/layout/dialog_enter_tan.xml
@@ -69,6 +69,15 @@
android:visibility="gone"
/>
+
+
-
-
-
-
-
+ android:layout_marginRight="@dimen/view_flicker_code_controls_sections_space"
+ android:layout_marginEnd="@dimen/view_flicker_code_controls_sections_space"/>
diff --git a/fints4javaAndroidApp/src/main/res/layout/view_tan_image.xml b/fints4javaAndroidApp/src/main/res/layout/view_tan_image.xml
new file mode 100644
index 00000000..564ca927
--- /dev/null
+++ b/fints4javaAndroidApp/src/main/res/layout/view_tan_image.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fints4javaAndroidApp/src/main/res/layout/view_tan_image_size_controls.xml b/fints4javaAndroidApp/src/main/res/layout/view_tan_image_size_controls.xml
new file mode 100644
index 00000000..f7dda2f8
--- /dev/null
+++ b/fints4javaAndroidApp/src/main/res/layout/view_tan_image_size_controls.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fints4javaAndroidApp/src/main/res/values/dimens.xml b/fints4javaAndroidApp/src/main/res/values/dimens.xml
index 9fcd51b4..70c78f56 100644
--- a/fints4javaAndroidApp/src/main/res/values/dimens.xml
+++ b/fints4javaAndroidApp/src/main/res/values/dimens.xml
@@ -39,8 +39,13 @@
50dp
120dp
- 40dp
- 40dp
+ 40dp
+ 40dp
+
+ 200dp
+ 4dp
+
+ 12dp
120dp
40dp
10dp
diff --git a/fints4javaAndroidApp/src/main/res/values/strings.xml b/fints4javaAndroidApp/src/main/res/values/strings.xml
index 08882430..798bd4de 100644
--- a/fints4javaAndroidApp/src/main/res/values/strings.xml
+++ b/fints4javaAndroidApp/src/main/res/values/strings.xml
@@ -50,9 +50,10 @@
Successfully transferred %1$s %2$s to %3$s.
Could not transfer %1$s %2$s to %3$s:\n%4$s.
- Size:
- +
- -
+ Size:
+ +
+ -
+
Frequency:
+
-
diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt
new file mode 100644
index 00000000..66f31424
--- /dev/null
+++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImage.kt
@@ -0,0 +1,13 @@
+package net.dankito.fints.tan
+
+
+open class TanImage(
+ val mimeType: String,
+ val imageBytes: ByteArray
+) {
+
+ override fun toString(): String {
+ return "$mimeType ${imageBytes.size} bytes"
+ }
+
+}
\ No newline at end of file
diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt
new file mode 100644
index 00000000..b0a039c8
--- /dev/null
+++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/tan/TanImageDecoder.kt
@@ -0,0 +1,45 @@
+package net.dankito.fints.tan
+
+import net.dankito.fints.messages.HbciCharset
+import org.slf4j.LoggerFactory
+
+
+open class TanImageDecoder {
+
+ companion object {
+ private val log = LoggerFactory.getLogger(TanImageDecoder::class.java)
+ }
+
+
+ open fun decodeChallenge(challengeHHD_UC: String): TanImage? {
+ try {
+ val bytes = challengeHHD_UC.toByteArray(HbciCharset.DefaultCharset)
+
+ val mimeTypeLength = getLength(bytes[0], bytes[1])
+ val mimeTypeEnd = 2 + mimeTypeLength
+
+ val mimeType = challengeHHD_UC.substring(2, mimeTypeEnd)
+
+ val imageLength = getLength(bytes[mimeTypeEnd], bytes[mimeTypeEnd + 1])
+ val imageStart = mimeTypeEnd + 2
+ val imageString = challengeHHD_UC.substring(imageStart, imageStart + imageLength)
+
+ val imageBytes = imageString.toByteArray(HbciCharset.DefaultCharset)
+
+ return TanImage(mimeType, imageBytes)
+ } catch (e: Exception) {
+ log.error("Could not decode challenge HHD_UC to TanImage: $challengeHHD_UC", e)
+ }
+
+ return null
+ }
+
+ protected open fun getLength(higherOrderByte: Byte, lowerOrderByte: Byte): Int {
+ return 256 * byteToUnsignedInt(higherOrderByte) + byteToUnsignedInt(lowerOrderByte)
+ }
+
+ protected open fun byteToUnsignedInt(byte: Byte): Int {
+ return byte.toUByte().toInt()
+ }
+
+}
\ No newline at end of file