Implemented displaying auto retrieved BIC and bank name in an info label; extracted InfoLabel from ValidationLabel

This commit is contained in:
dankito 2020-08-24 13:43:51 +02:00
parent 5d88e51ab9
commit f3d7afc376
11 changed files with 117 additions and 90 deletions

View File

@ -59,6 +59,9 @@ open class TransferMoneyDialog : DialogFragment() {
protected val inputValidator = InputValidator() // TODO: move to presenter protected val inputValidator = InputValidator() // TODO: move to presenter
protected var remitteeBic: String? = null
protected var validRemitteeNameEntered = false protected var validRemitteeNameEntered = false
protected var validRemitteeIbanEntered = false protected var validRemitteeIbanEntered = false
@ -133,7 +136,6 @@ open class TransferMoneyDialog : DialogFragment() {
tryToGetBicFromIban(it) tryToGetBicFromIban(it)
}) })
rootView.edtxtRemitteeBic.addTextChangedListener(checkRequiredDataWatcher())
rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher()) rootView.edtxtAmount.addTextChangedListener(checkRequiredDataWatcher())
rootView.edtxtUsage.addTextChangedListener(checkRequiredDataWatcher { rootView.edtxtUsage.addTextChangedListener(checkRequiredDataWatcher {
checkIfEnteredUsageTextIsValid() checkIfEnteredUsageTextIsValid()
@ -141,13 +143,11 @@ open class TransferMoneyDialog : DialogFragment() {
rootView.edtxtRemitteeName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeNameIsValidAfterFocusLost() } rootView.edtxtRemitteeName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeNameIsValidAfterFocusLost() }
rootView.edtxtRemitteeIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeIbanIsValidAfterFocusLost() } rootView.edtxtRemitteeIban.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeIbanIsValidAfterFocusLost() }
rootView.edtxtRemitteeBic.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredRemitteeBicIsValid() }
rootView.edtxtAmount.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredAmountIsValid() } rootView.edtxtAmount.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredAmountIsValid() }
rootView.edtxtUsage.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredUsageTextIsValid() } rootView.edtxtUsage.setOnFocusChangeListener { _, hasFocus -> if (hasFocus == false) checkIfEnteredUsageTextIsValid() }
transferMoneyIfEnterPressed(rootView.edtxtRemitteeName) transferMoneyIfEnterPressed(rootView.edtxtRemitteeName)
transferMoneyIfEnterPressed(rootView.edtxtRemitteeIban) transferMoneyIfEnterPressed(rootView.edtxtRemitteeIban)
transferMoneyIfEnterPressed(rootView.edtxtRemitteeBic)
transferMoneyIfEnterPressed(rootView.edtxtAmount) transferMoneyIfEnterPressed(rootView.edtxtAmount)
transferMoneyIfEnterPressed(rootView.edtxtUsage) transferMoneyIfEnterPressed(rootView.edtxtUsage)
@ -218,7 +218,7 @@ open class TransferMoneyDialog : DialogFragment() {
} }
// a little bit inconsistent as if IBAN is not set bank's name won't be displayed even though it can be retrieved by BIC // a little bit inconsistent as if IBAN is not set bank's name won't be displayed even though it can be retrieved by BIC
rootView.edtxtRemitteeBic.setText(data.creditorBic) remitteeBic = data.creditorBic
if (data.amount > BigDecimal.ZERO) { if (data.amount > BigDecimal.ZERO) {
rootView.edtxtAmount.setText(AmountFormat.format(data.amount)) rootView.edtxtAmount.setText(AmountFormat.format(data.amount))
@ -231,12 +231,7 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun focusEditTextAccordingToPreselectedValues(rootView: View, data: TransferMoneyData) { protected open fun focusEditTextAccordingToPreselectedValues(rootView: View, data: TransferMoneyData) {
if (data.creditorName.trim().isNotEmpty()) { if (data.creditorName.trim().isNotEmpty()) {
if (data.creditorIban.trim().isNotEmpty()) { if (data.creditorIban.trim().isNotEmpty()) {
if (data.creditorBic.trim().isNotEmpty()) { rootView.edtxtAmount.requestFocus()
rootView.edtxtAmount.requestFocus()
}
else {
rootView.edtxtRemitteeBic.requestFocus()
}
} }
else { else {
rootView.edtxtRemitteeIban.requestFocus() rootView.edtxtRemitteeIban.requestFocus()
@ -247,8 +242,8 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun remitteeSelected(item: Remittee) { protected open fun remitteeSelected(item: Remittee) {
edtxtRemitteeName.setText(item.name) edtxtRemitteeName.setText(item.name)
edtxtRemitteeBic.setText(item.bic)
edtxtRemitteeIban.setText(item.iban) edtxtRemitteeIban.setText(item.iban)
remitteeBic = item.bic
} }
protected open fun transferMoney() { protected open fun transferMoney() {
@ -256,7 +251,7 @@ open class TransferMoneyDialog : DialogFragment() {
val data = TransferMoneyData( val data = TransferMoneyData(
inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtRemitteeName.text.toString()),
edtxtRemitteeIban.text.toString().replace(" ", ""), edtxtRemitteeIban.text.toString().replace(" ", ""),
edtxtRemitteeBic.text.toString().replace(" ", ""), remitteeBic?.replace(" ", "") ?: "", // should always be != null at this point
amount.toBigDecimal(), amount.toBigDecimal(),
inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()), inputValidator.convertToAllowedSepaCharacters(edtxtUsage.text.toString()),
chkbxInstantPayment.isChecked chkbxInstantPayment.isChecked
@ -308,18 +303,26 @@ open class TransferMoneyDialog : DialogFragment() {
protected open fun tryToGetBicFromIban(enteredIban: CharSequence) { protected open fun tryToGetBicFromIban(enteredIban: CharSequence) {
presenter.findUniqueBankForIbanAsync(enteredIban.toString()) { foundBank -> presenter.findUniqueBankForIbanAsync(enteredIban.toString()) { foundBank ->
context?.asActivity()?.runOnUiThread { context?.asActivity()?.runOnUiThread {
showValuesForFoundBankOnUiThread(foundBank) showValuesForFoundBankOnUiThread(enteredIban, foundBank)
} }
} }
} }
private fun showValuesForFoundBankOnUiThread(foundBank: BankInfo?) { private fun showValuesForFoundBankOnUiThread(enteredIban: CharSequence, foundBank: BankInfo?) {
validRemitteeBicEntered = foundBank != null validRemitteeBicEntered = foundBank != null
remitteeBic = foundBank?.bic
edtxtRemitteeBank.setText(if (foundBank != null) (foundBank.name + " " + foundBank.city) else "") if (foundBank != null) {
txtRemitteeBankInfo.text = getString(R.string.dialog_transfer_money_bic_detected_from_iban, foundBank.bic, foundBank.name)
edtxtRemitteeBic.setText(foundBank?.bic ?: "") // TODO: check if user entered BIC to not overwrite self entered BIC txtRemitteeBankInfo.visibility = View.VISIBLE
lytRemitteeBic.error = null // TODO: show information here if BIC hasn't been found }
else if (enteredIban.length >= InputValidator.MinimumLengthToDetermineBicFromIban) {
txtRemitteeBankInfo.text = getString(R.string.dialog_transfer_money_could_not_determine_bic_from_iban, enteredIban.substring(4, InputValidator.MinimumLengthToDetermineBicFromIban))
txtRemitteeBankInfo.visibility = View.VISIBLE
}
else {
txtRemitteeBankInfo.visibility = View.GONE
}
checkIfRequiredDataEnteredOnUiThread() checkIfRequiredDataEnteredOnUiThread()
} }
@ -357,13 +360,6 @@ open class TransferMoneyDialog : DialogFragment() {
this.validRemitteeIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString this.validRemitteeIbanEntered = validationResult.validationSuccessfulOrCouldCorrectString
showValidationResult(lytRemitteeIban, validationResult) showValidationResult(lytRemitteeIban, validationResult)
if (validRemitteeBicEntered || enteredIban.isBlank()) {
lytRemitteeBic.error = null
}
else {
lytRemitteeBic.error = context?.getString(R.string.error_no_bank_found_for_entered_iban)
}
} }
protected open fun checkIfEnteredRemitteeIbanIsValidAfterFocusLost() { protected open fun checkIfEnteredRemitteeIbanIsValidAfterFocusLost() {
@ -376,15 +372,6 @@ open class TransferMoneyDialog : DialogFragment() {
} }
} }
protected open fun checkIfEnteredRemitteeBicIsValid() {
val enteredBic = edtxtRemitteeBic.text.toString()
val validationResult = inputValidator.validateBic(enteredBic)
this.validRemitteeBicEntered = validationResult.validationSuccessfulOrCouldCorrectString
showValidationResult(lytRemitteeBic, validationResult)
}
protected open fun checkIfEnteredAmountIsValid() { protected open fun checkIfEnteredAmountIsValid() {
val validationResult = inputValidator.validateAmount(edtxtAmount.text.toString()) val validationResult = inputValidator.validateAmount(edtxtAmount.text.toString())

View File

@ -79,41 +79,14 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<TextView
<com.google.android.material.textfield.TextInputLayout android:id="@+id/txtRemitteeBankInfo"
android:id="@+id/lytRemitteeBank"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/dialog_transfer_money_remittee_bank" style="@style/TextAppearance.AppCompat.Small"
> android:visibility="gone"
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtxtRemitteeBank"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
android:inputType="text"
android:enabled="false"
/> />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/lytRemitteeBic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/dialog_transfer_money_remittee_bic"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtxtRemitteeBic"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_transfer_money_input_fields_height"
android:inputType="text"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/lytAmount" android:id="@+id/lytAmount"

View File

@ -52,8 +52,8 @@
<string name="dialog_transfer_money_account">Konto:</string> <string name="dialog_transfer_money_account">Konto:</string>
<string name="dialog_transfer_money_remittee_name">Name:</string> <string name="dialog_transfer_money_remittee_name">Name:</string>
<string name="dialog_transfer_money_remittee_iban">IBAN:</string> <string name="dialog_transfer_money_remittee_iban">IBAN:</string>
<string name="dialog_transfer_money_remittee_bank">Bank (wird automatisch eingetragen):</string> <string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string>
<string name="dialog_transfer_money_remittee_bic">BIC (wird automatisch eingetragen):</string> <string name="dialog_transfer_money_could_not_determine_bic_from_iban">Keine BIC gefunden für BLZ %1$s</string>
<string name="dialog_transfer_money_amount">Betrag:</string> <string name="dialog_transfer_money_amount">Betrag:</string>
<string name="dialog_transfer_money_usage">Verwendungszweck:</string> <string name="dialog_transfer_money_usage">Verwendungszweck:</string>
<string name="dialog_transfer_money_instant_payment">Echtzeitüberweisung</string> <string name="dialog_transfer_money_instant_payment">Echtzeitüberweisung</string>
@ -103,8 +103,6 @@
<string name="error_no_iban_entered">Bitte geben Sie die IBAN des Empfängers ein</string> <string name="error_no_iban_entered">Bitte geben Sie die IBAN des Empfängers ein</string>
<string name="error_invalid_iban_characters_entered">Unzulässige(s) Zeichen eingegeben: %s</string> <string name="error_invalid_iban_characters_entered">Unzulässige(s) Zeichen eingegeben: %s</string>
<string name="error_invalid_iban_pattern_entered">IBANs bestehen aus folgendem Muster: DE12 1234 5678 9012 3456 78</string> <string name="error_invalid_iban_pattern_entered">IBANs bestehen aus folgendem Muster: DE12 1234 5678 9012 3456 78</string>
<string name="error_no_bank_found_for_entered_iban">Es wurde keine Bank zur eingegebenen IBAN gefunden.</string>
<string name="error_no_bic_entered">Bitte geben Sie die BIC des Empfängers ein</string>
<string name="error_invalid_bic_characters_entered">Unzulässige(s) Zeichen eingegeben: %s</string> <string name="error_invalid_bic_characters_entered">Unzulässige(s) Zeichen eingegeben: %s</string>
<string name="error_invalid_bic_pattern_entered">Eine BIC besteht aus 8 oder 11 Zeichen und folgt dem Muster: ABCDED12(XYZ)</string> <string name="error_invalid_bic_pattern_entered">Eine BIC besteht aus 8 oder 11 Zeichen und folgt dem Muster: ABCDED12(XYZ)</string>
<string name="error_no_amount_entered">Bitte geben Sie den zu überweisenden Betrag ein</string> <string name="error_no_amount_entered">Bitte geben Sie den zu überweisenden Betrag ein</string>

View File

@ -52,8 +52,8 @@
<string name="dialog_transfer_money_account">Account:</string> <string name="dialog_transfer_money_account">Account:</string>
<string name="dialog_transfer_money_remittee_name">Name:</string> <string name="dialog_transfer_money_remittee_name">Name:</string>
<string name="dialog_transfer_money_remittee_iban">IBAN:</string> <string name="dialog_transfer_money_remittee_iban">IBAN:</string>
<string name="dialog_transfer_money_remittee_bank">Bank (will be entered automatically):</string> <string name="dialog_transfer_money_bic_detected_from_iban">BIC: %1$s, %2$s</string>
<string name="dialog_transfer_money_remittee_bic">BIC (will be entered automatically):</string> <string name="dialog_transfer_money_could_not_determine_bic_from_iban">No BIC found for bank code %1$s</string>
<string name="dialog_transfer_money_amount">Amount:</string> <string name="dialog_transfer_money_amount">Amount:</string>
<string name="dialog_transfer_money_usage">Usage:</string> <string name="dialog_transfer_money_usage">Usage:</string>
<string name="dialog_transfer_money_instant_payment">Instant payment</string> <string name="dialog_transfer_money_instant_payment">Instant payment</string>
@ -103,8 +103,6 @@
<string name="error_no_iban_entered">Please enter remittee\'s IBAN</string> <string name="error_no_iban_entered">Please enter remittee\'s IBAN</string>
<string name="error_invalid_iban_characters_entered">Invalid character(s) entered: %s</string> <string name="error_invalid_iban_characters_entered">Invalid character(s) entered: %s</string>
<string name="error_invalid_iban_pattern_entered">IBAN has to have pattern: EN12 1234 5678 9012 3456 78</string> <string name="error_invalid_iban_pattern_entered">IBAN has to have pattern: EN12 1234 5678 9012 3456 78</string>
<string name="error_no_bank_found_for_entered_iban">No bank found for entered IBAN.</string>
<string name="error_no_bic_entered">Please enter remittee\'s BIC</string>
<string name="error_invalid_bic_characters_entered">Invalid character(s) entered: %s</string> <string name="error_invalid_bic_characters_entered">Invalid character(s) entered: %s</string>
<string name="error_invalid_bic_pattern_entered">A BIC consists of 8 or 11 characters and has the pattern: ABCDED12(XYZ)</string> <string name="error_invalid_bic_pattern_entered">A BIC consists of 8 or 11 characters and has the pattern: ABCDED12(XYZ)</string>
<string name="error_no_amount_entered">Please enter the amount to be transferred</string> <string name="error_no_amount_entered">Please enter the amount to be transferred</string>

View File

@ -17,6 +17,8 @@ open class InputValidator {
const val UsageMaxLength = 140 const val UsageMaxLength = 140
const val MinimumLengthToDetermineBicFromIban = 12 // TODO: this is only true for German (and may some other) IBANs
/** /**
* The IBAN consists of up to 34 alphanumeric characters, as follows: * The IBAN consists of up to 34 alphanumeric characters, as follows:

View File

@ -72,6 +72,8 @@
"Transfer Money Dialog Title" = "Bank transfer"; "Transfer Money Dialog Title" = "Bank transfer";
"Remittee Name" = "Name"; "Remittee Name" = "Name";
"Remittee IBAN" = "IBAN"; "Remittee IBAN" = "IBAN";
"BIC: %@, %@" = "BIC: %@, %@";
"No BIC found for bank code %@" = "No BIC found for bank code %@";
"Amount" = "Amount"; "Amount" = "Amount";
"Usage" = "Usage"; "Usage" = "Usage";
"Instant Payment" = "Instant Payment"; "Instant Payment" = "Instant Payment";

View File

@ -72,6 +72,8 @@
"Transfer Money Dialog Title" = "Überweisung"; "Transfer Money Dialog Title" = "Überweisung";
"Remittee Name" = "Name"; "Remittee Name" = "Name";
"Remittee IBAN" = "IBAN"; "Remittee IBAN" = "IBAN";
"BIC: %@, %@" = "BIC: %@, %@";
"No BIC found for bank code %@" = "Keine BIC gefunden für BLZ %@";
"Amount" = "Betrag"; "Amount" = "Betrag";
"Usage" = "Verwendungszweck"; "Usage" = "Verwendungszweck";
"Instant Payment" = "Echtzeitüberweisung"; "Instant Payment" = "Echtzeitüberweisung";

View File

@ -0,0 +1,47 @@
import SwiftUI
struct InfoLabel: View {
private let information: LocalizedStringKey
init(_ information: String) {
self.init(LocalizedStringKey(information))
}
init(_ information: LocalizedStringKey) {
self.information = information
}
var body: some View {
VStack {
Spacer()
.frame(height: 6)
HStack {
Text(information)
.padding(.leading, 16)
Spacer()
}
Spacer()
.frame(height: 18)
}
.font(.callout)
.systemGroupedBackground()
.listRowInsets(EdgeInsets())
}
}
struct InfoLabel_Previews: PreviewProvider {
static var previews: some View {
InfoLabel("A nice info")
}
}

View File

@ -20,6 +20,25 @@ extension String {
return NSLocalizedString(self, comment: "") return NSLocalizedString(self, comment: "")
} }
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
subscript (r: Range<Int>) -> String {
let start = index(startIndex, offsetBy: r.lowerBound)
let end = index(startIndex, offsetBy: r.upperBound)
return String(self[start ..< end])
}
subscript (r: CountableClosedRange<Int>) -> String {
let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
return String(self[startIndex...endIndex])
}
} }
extension Optional where Wrapped == String { extension Optional where Wrapped == String {

View File

@ -25,24 +25,8 @@ struct ValidationLabel: View {
var body: some View { var body: some View {
VStack { InfoLabel(validationErrorOrHint)
Spacer()
.frame(height: 6)
HStack {
Text(validationErrorOrHint)
.padding(.leading, 16)
Spacer()
}
Spacer()
.frame(height: 18)
}
.font(.callout)
.foregroundColor(isHint ? Color.yellow : Color.red) .foregroundColor(isHint ? Color.yellow : Color.red)
.systemGroupedBackground()
.listRowInsets(EdgeInsets())
} }
} }

View File

@ -29,6 +29,8 @@ struct TransferMoneyDialog: View {
@State private var isValidRemitteeBicEntered = false @State private var isValidRemitteeBicEntered = false
@State private var remitteeBicValidationResult: ValidationResult? = nil @State private var remitteeBicValidationResult: ValidationResult? = nil
@State private var remitteeBankInfo: String? = nil
@State private var amount = "" @State private var amount = ""
@State private var isValidAmountEntered = false @State private var isValidAmountEntered = false
@State private var amountValidationResult: ValidationResult? = nil @State private var amountValidationResult: ValidationResult? = nil
@ -127,6 +129,11 @@ struct TransferMoneyDialog: View {
remitteeIbanValidationResult.map { validationError in remitteeIbanValidationResult.map { validationError in
ValidationLabel(validationError) ValidationLabel(validationError)
} }
remitteeBankInfo.map {
InfoLabel($0)
.font(.caption)
}
} }
Section { Section {
@ -215,9 +222,17 @@ struct TransferMoneyDialog: View {
if let foundBank = foundBank { if let foundBank = foundBank {
self.remitteeBic = foundBank.bic self.remitteeBic = foundBank.bic
self.remitteeBankInfo = "BIC: \(foundBank.bic), \(foundBank.name)"
} }
else { else {
self.remitteeBic = "" self.remitteeBic = ""
if enteredIban.count >= InputValidator.Companion().MinimumLengthToDetermineBicFromIban {
self.remitteeBankInfo = "No BIC found for bank code \(enteredIban[4..<Int(InputValidator.Companion().MinimumLengthToDetermineBicFromIban)])"
}
else {
self.remitteeBankInfo = nil
}
} }
// TODO: implement a better check if entered BIC is valid (e.g. if format is ABCDDEXX123) // TODO: implement a better check if entered BIC is valid (e.g. if format is ABCDDEXX123)