Implemented parsing and displaying images for chipTan-QR and PhotoTan; extracted TanImageSizeControlView
This commit is contained in:
parent
0660498ceb
commit
aa88c751dd
|
@ -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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -69,6 +69,15 @@
|
|||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<net.dankito.banking.fints4java.android.ui.views.TanImageView
|
||||
android:id="@+id/tanImageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTanDescriptionToShowToUser"
|
||||
|
|
|
@ -9,31 +9,17 @@
|
|||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
>
|
||||
|
||||
<TextView
|
||||
<net.dankito.banking.fints4java.android.ui.views.TanImageSizeControlsView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/view_flicker_code_size"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnIncreaseSize"
|
||||
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
|
||||
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
|
||||
android:text="@string/view_flicker_code_increase_size"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDecreaseSize"
|
||||
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
|
||||
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
|
||||
android:text="@string/view_flicker_code_decrease_size"
|
||||
/>
|
||||
android:layout_marginRight="@dimen/view_flicker_code_controls_sections_space"
|
||||
android:layout_marginEnd="@dimen/view_flicker_code_controls_sections_space"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -44,15 +30,15 @@
|
|||
|
||||
<Button
|
||||
android:id="@+id/btnIncreaseSpeed"
|
||||
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
|
||||
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
|
||||
android:layout_width="@dimen/view_tan_image_controls_buttons_width"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:text="@string/view_flicker_code_increase_frequency"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDecreaseSpeed"
|
||||
android:layout_width="@dimen/view_flicker_code_control_buttons_width"
|
||||
android:layout_height="@dimen/view_flicker_code_control_buttons_height"
|
||||
android:layout_width="@dimen/view_tan_image_controls_buttons_width"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:text="@string/view_flicker_code_decrease_frequency"
|
||||
/>
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
>
|
||||
|
||||
<net.dankito.banking.fints4java.android.ui.views.TanImageSizeControlsView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgTanImageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/view_tan_image_image_height"
|
||||
android:layout_gravity="center"
|
||||
android:paddingTop="@dimen/view_tan_image_image_margin_top_bottom"
|
||||
android:paddingBottom="@dimen/view_tan_image_image_margin_top_bottom"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/view_tan_image_size_controls_size"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnIncreaseSize"
|
||||
android:layout_width="@dimen/view_tan_image_controls_buttons_width"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:text="@string/view_tan_image_size_controls_increase_size"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDecreaseSize"
|
||||
android:layout_width="@dimen/view_tan_image_controls_buttons_width"
|
||||
android:layout_height="@dimen/view_tan_image_controls_buttons_height"
|
||||
android:text="@string/view_tan_image_size_controls_decrease_size"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
|
@ -39,8 +39,13 @@
|
|||
<dimen name="dialog_bank_transfer_autocomplete_fields_height">50dp</dimen>
|
||||
<dimen name="dialog_bank_transfer_buttons_width">120dp</dimen>
|
||||
|
||||
<dimen name="view_flicker_code_control_buttons_height">40dp</dimen>
|
||||
<dimen name="view_flicker_code_control_buttons_width">40dp</dimen>
|
||||
<dimen name="view_tan_image_controls_buttons_height">40dp</dimen>
|
||||
<dimen name="view_tan_image_controls_buttons_width">40dp</dimen>
|
||||
|
||||
<dimen name="view_tan_image_image_height">200dp</dimen>
|
||||
<dimen name="view_tan_image_image_margin_top_bottom">4dp</dimen>
|
||||
|
||||
<dimen name="view_flicker_code_controls_sections_space">12dp</dimen>
|
||||
<dimen name="view_flicker_code_stripe_height">120dp</dimen>
|
||||
<dimen name="view_flicker_code_stripe_width">40dp</dimen>
|
||||
<dimen name="view_flicker_code_stripe_margin_right">10dp</dimen>
|
||||
|
|
|
@ -50,9 +50,10 @@
|
|||
<string name="dialog_bank_transfer_message_transfer_successful">Successfully transferred %1$s %2$s to %3$s.</string>
|
||||
<string name="dialog_bank_transfer_message_transfer_failed">Could not transfer %1$s %2$s to %3$s:\n%4$s.</string>
|
||||
|
||||
<string name="view_flicker_code_size">Size:</string>
|
||||
<string name="view_flicker_code_increase_size">+</string>
|
||||
<string name="view_flicker_code_decrease_size">-</string>
|
||||
<string name="view_tan_image_size_controls_size">Size:</string>
|
||||
<string name="view_tan_image_size_controls_increase_size">+</string>
|
||||
<string name="view_tan_image_size_controls_decrease_size">-</string>
|
||||
|
||||
<string name="view_flicker_code_frequency">Frequency:</string>
|
||||
<string name="view_flicker_code_increase_frequency">+</string>
|
||||
<string name="view_flicker_code_decrease_frequency">-</string>
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue