Implemented sending message log

This commit is contained in:
dankito 2020-05-16 22:51:51 +02:00
parent bcfe431110
commit 685e4209c3
17 changed files with 295 additions and 29 deletions

View File

@ -64,8 +64,9 @@ open class FinTsClient @JvmOverloads constructor(
protected val messageLogField = CopyOnWriteArrayList<MessageLogEntry>()
open val messageLog: List<MessageLogEntry>
get() = ArrayList(messageLogField)
// in either case remove sensitive data after response is parsed as otherwise some information like account holder name and accounts may is not set yet on CustomerData
open val messageLogWithoutSensitiveData: List<MessageLogEntry>
get() = messageLogField.map { MessageLogEntry(removeSensitiveDataFromMessage(it.message, it.customer), it.time, it.customer) }
/**
@ -648,29 +649,32 @@ open class FinTsClient @JvmOverloads constructor(
protected open fun addMessageLog(message: String, type: MessageLogEntryType, dialogContext: DialogContext) {
val timeStamp = Date()
val messagePrefix = "${if (type == MessageLogEntryType.Sent) "Sending" else "Received"} message:\r\n" // currently no need to translate
val prettyPrintMessage = prettyPrintHbciMessage(message)
val prettyPrintMessageWithoutSensitiveData = removeSensitiveDataFromMessage(prettyPrintMessage, dialogContext)
val prettyPrintMessageWithPrefix = "$messagePrefix$prettyPrintMessage"
if (log.isDebugEnabled) {
log.debug("${if (type == MessageLogEntryType.Sent) "Sending" else "Received"} message:\n$prettyPrintMessage")
log.debug(prettyPrintMessageWithPrefix)
}
messageLogField.add(MessageLogEntry(prettyPrintMessageWithoutSensitiveData, timeStamp, dialogContext.customer))
messageLogField.add(MessageLogEntry(prettyPrintMessageWithPrefix, timeStamp, dialogContext.customer))
}
protected open fun removeSensitiveDataFromMessage(prettyPrintMessage: String, dialogContext: DialogContext): String {
var prettyPrintMessageWithoutSensitiveData = prettyPrintMessage
.replace(dialogContext.customer.customerId, "<customer_id>")
.replace("+" + dialogContext.customer.pin, "+<pin>")
protected fun prettyPrintHbciMessage(message: String): String {
return message.replace("'", "'\r\n")
}
if (dialogContext.customer.name.isNotBlank()) { // TODO: log after response is parsed as otherwise this information may is not available
protected open fun removeSensitiveDataFromMessage(message: String, customer: CustomerData): String {
var prettyPrintMessageWithoutSensitiveData = message
.replace(customer.customerId, "<customer_id>")
.replace("+" + customer.pin, "+<pin>")
if (customer.name.isNotBlank()) {
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
.replace(dialogContext.customer.name, "<customer_name>", true)
.replace(customer.name, "<customer_name>", true)
}
dialogContext.customer.accounts.forEach { account ->
customer.accounts.forEach { account ->
prettyPrintMessageWithoutSensitiveData = prettyPrintMessageWithoutSensitiveData
.replace(account.accountIdentifier, "<account_identifier>")

View File

@ -32,6 +32,10 @@ open class FinTsClientForCustomer @JvmOverloads constructor(
protected val client = FinTsClient(callback, base64Service, webClient, messageBuilder, responseParser, mt940Parser, threadPool, product)
open val messageLogWithoutSensitiveData: List<MessageLogEntry>
get() = client.messageLogWithoutSensitiveData
open fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
client.addAccountAsync(bank, customer, callback)
}

View File

@ -1,9 +1,6 @@
package net.dankito.banking.fints4java.android
import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterAtcDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog
import net.dankito.banking.fints4java.android.ui.dialogs.TransferMoneyDialog
import net.dankito.banking.fints4java.android.ui.dialogs.*
import net.dankito.banking.fints4java.android.ui.util.CurrentActivityTracker
import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.model.Account
@ -68,4 +65,12 @@ open class RouterAndroid(protected val activityTracker: CurrentActivityTracker)
}
}
override fun showSendMessageLogDialog(presenter: BankingPresenter) {
activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread {
SendMessageLogDialog().show(activity)
}
}
}
}

View File

@ -5,6 +5,7 @@ import net.dankito.banking.fints4java.android.MainActivity
import net.dankito.banking.fints4java.android.ui.activities.BaseActivity
import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog
import net.dankito.banking.fints4java.android.ui.dialogs.SendMessageLogDialog
import net.dankito.banking.fints4java.android.ui.dialogs.TransferMoneyDialog
import net.dankito.banking.fints4java.android.ui.home.HomeFragment
import javax.inject.Singleton
@ -31,4 +32,6 @@ interface BankingComponent {
fun inject(transferMoneyDialog: TransferMoneyDialog)
fun inject(sendMessageLogDialog: SendMessageLogDialog)
}

View File

@ -0,0 +1,87 @@
package net.dankito.banking.fints4java.android.ui.dialogs
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_send_message_log.view.*
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.fints4java.android.di.BankingComponent
import net.dankito.banking.ui.presenter.BankingPresenter
import javax.inject.Inject
open class SendMessageLogDialog : DialogFragment() {
companion object {
const val DialogTag = "SendMessageLogDialog"
}
@Inject
protected lateinit var presenter: BankingPresenter
init {
BankingComponent.component.inject(this)
}
fun show(activity: AppCompatActivity, fullscreen: Boolean = false) {
val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog
setStyle(STYLE_NORMAL, style)
show(activity.supportFragmentManager, DialogTag)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.dialog_send_message_log, container, false)
rootView?.let {
setupUI(rootView)
}
return rootView
}
protected open fun setupUI(rootView: View) {
val messageLog = presenter.getMessageLogForAccounts(presenter.accounts).joinToString("\r\n\r\n")
if (messageLog.isBlank()) {
rootView.txtvwInfoNoMessageLogEntriesYet.visibility = View.VISIBLE
rootView.lytMessageLog.visibility = View.GONE
}
else {
rootView.edtxtMessageLog.setText(context?.getString(R.string.dialog_send_message_courteously_add_error_description, messageLog))
}
rootView.btnSendMessageLog.setOnClickListener { sendMessageLog(rootView.edtxtMessageLog.text.toString()) }
rootView.btnCancel.setOnClickListener { dismiss() }
}
protected open fun sendMessageLog(messageLog: String) {
val sendMailAction = Intent(Intent.ACTION_SEND)
sendMailAction.type = "message/rfc822"
sendMailAction.putExtra(Intent.EXTRA_EMAIL, arrayOf("panta.rhei@dankito.net"))
sendMailAction.putExtra(Intent.EXTRA_SUBJECT, context?.getString(R.string.dialog_send_message_log_mail_subject))
sendMailAction.putExtra(Intent.EXTRA_TEXT, messageLog)
try {
startActivity(Intent.createChooser(sendMailAction, context?.getString(R.string.dialog_send_message_log_action_send_chooser_title)))
Toast.makeText(context, context?.getString(R.string.dialog_send_message_log_thanks_for_helping_making_app_better), Toast.LENGTH_LONG).show()
dismiss()
} catch (e: ActivityNotFoundException) {
Toast.makeText(context, context?.getString(R.string.dialog_send_message_log_error_no_app_to_send_message_found), Toast.LENGTH_LONG).show()
}
}
}

View File

@ -8,6 +8,7 @@ import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
@ -39,7 +40,8 @@ open class DrawerView(
private const val AllAccountsId = 1001L
private const val AddAccountId = 1002L
private const val CountDefaultAccountItems = 3
private const val CountDefaultAccountItems = 4
private const val CountDefaultAccountItemsAtTop = 2
private val log = LoggerFactory.getLogger(DrawerView::class.java)
@ -94,6 +96,18 @@ open class DrawerView(
.withIcon(activity, GoogleMaterial.Icon.gmd_add, R.color.primaryTextColor_Dark)
.withSelectable(false)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showAddAccountDialog() } }
,
DividerDrawerItem()
.withSelectable(false)
,
PrimaryDrawerItem()
.withName(R.string.drawer_menu_send_message_log_title)
.withIcon(activity, GoogleMaterial.Icon.gmd_mail, R.color.primaryTextColor_Dark)
.withSelectable(false)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showSendMessageLogDialog() } }
)
}
}
@ -101,12 +115,12 @@ open class DrawerView(
private fun updateDrawerItems() {
// removes previously shown accounts; index 1 = 'Accounts header', 1 = 'All accounts', index 2 = 'Add account', don't remove these
while (slider.itemAdapter.adapterItems.size > CountDefaultAccountItems) {
slider.removeItemByPosition(CountDefaultAccountItems)
slider.removeItemByPosition(CountDefaultAccountItemsAtTop)
}
val accountItems = createAccountsDrawerItems()
slider.addItemsAtPosition(CountDefaultAccountItems, *accountItems.toTypedArray())
slider.addItemsAtPosition(CountDefaultAccountItemsAtTop, *accountItems.toTypedArray())
slider.getDrawerItem(AllAccountsId)?.let { allAccountsItem ->
if (presenter.areAllAccountSelected) slider.selectExtension.select(allAccountsItem, false)

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/dialog_send_message_log_padding"
>
<TextView
android:id="@+id/txtMessageLogLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginBottom="@dimen/dialog_send_message_log_message_log_label_bottom_margin"
style="@style/TextAppearance.AppCompat.Medium"
android:text="@string/dialog_send_message_log_message_log_label"
/>
<TextView
android:id="@+id/txtvwInfoNoMessageLogEntriesYet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/txtMessageLogLabel"
android:layout_above="@+id/lytButtonBar"
style="@style/TextAppearance.AppCompat.Medium"
android:text="@string/dialog_send_message_log_info_no_message_log_entries_yet"
android:visibility="gone"
/>
<ScrollView
android:id="@+id/lytMessageLog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/txtMessageLogLabel"
android:layout_above="@+id/lytButtonBar"
>
<EditText
android:id="@+id/edtxtMessageLog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Small"
/>
</ScrollView>
<RelativeLayout
android:id="@+id/lytButtonBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
>
<Button
android:id="@+id/btnSendMessageLog"
android:layout_width="@dimen/dialog_send_message_log_buttons_width"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/dialog_send_message_log_send"
/>
<Button
android:id="@+id/btnCancel"
android:layout_width="@dimen/dialog_send_message_log_buttons_width"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/btnSendMessageLog"
android:layout_toStartOf="@+id/btnSendMessageLog"
style="?android:attr/buttonBarButtonStyle"
android:text="@string/cancel"
/>
</RelativeLayout>
</RelativeLayout>

View File

@ -19,6 +19,7 @@
<string name="drawer_menu_bank_accounts_section_title">Konten</string>
<string name="drawer_menu_add_bank_account_title">Konto hinzufügen</string>
<string name="drawer_menu_all_bank_accounts_title">Alle Konten</string>
<string name="drawer_menu_send_message_log_title">Message Log senden</string>
<string name="floating_action_menu_add_account">Konto</string>
<string name="floating_action_menu_transfer_money">Überweisung</string>
@ -105,4 +106,14 @@
<string name="error_invalid_amount_entered">Bitte geben Sie einen Betrag größer 0 ein.</string>
<string name="error_entered_usage_too_long">Verwendungszweck darf nur 140 Zeichen lang sein</string>
<string name="dialog_send_message_log_message_log_label">Message Log</string>
<string name="dialog_send_message_log_send">Senden</string>
<string name="dialog_send_message_log_info_no_message_log_entries_yet">Noch keine Message Log Einträge vorhanden. Führen Sie erst ein paar Aktionen durch, z. B. Abholen der Umsätze.</string>
<string name="dialog_send_message_courteously_add_error_description">Könnten Sie noch eine kurze Beschreibung des aufgetretenen Fehlers hinzufügen (z. B. was Sie zuvor in der App gemacht haben oder welches TAN Verfahren Sie verwendet haben)?\n\n\n%s</string>
<string name="dialog_send_message_log_mail_subject">Message Log</string>
<string name="dialog_send_message_log_action_send_chooser_title">Message Log senden …</string>
<string name="dialog_send_message_log_thanks_for_helping_making_app_better">Vielen Dank, dass sie uns dabei helfen die App besser zu machen!</string>
<string name="dialog_send_message_log_error_no_app_to_send_message_found">Keine E-Mail App zum Senden des Message Logs gefunden.</string>
</resources>

View File

@ -100,4 +100,8 @@
<dimen name="dialog_enter_atc_enter_value_field_value_margin_left">6dp</dimen>
<dimen name="dialog_enter_atc_buttons_width">120dp</dimen>
<dimen name="dialog_send_message_log_padding">4dp</dimen>
<dimen name="dialog_send_message_log_message_log_label_bottom_margin">8dp</dimen>
<dimen name="dialog_send_message_log_buttons_width">120dp</dimen>
</resources>

View File

@ -19,6 +19,7 @@
<string name="drawer_menu_bank_accounts_section_title">Accounts</string>
<string name="drawer_menu_all_bank_accounts_title">All accounts</string>
<string name="drawer_menu_add_bank_account_title">Add account</string>
<string name="drawer_menu_send_message_log_title">Send message log</string>
<string name="floating_action_menu_add_account">Account</string>
<string name="floating_action_menu_transfer_money">Transfer money</string>
@ -105,4 +106,14 @@
<string name="error_invalid_amount_entered">Please enter an amount greater zero.</string>
<string name="error_entered_usage_too_long">Usage may only have 140 characters</string>
<string name="dialog_send_message_log_message_log_label">Message log</string>
<string name="dialog_send_message_log_send">Send</string>
<string name="dialog_send_message_log_info_no_message_log_entries_yet">No message log entries yet. First do some actions like requesting account transactions from bank.</string>
<string name="dialog_send_message_courteously_add_error_description">Could you courteously add a short description of the occurred error?\n\n\n%s</string>
<string name="dialog_send_message_log_mail_subject">Message Log</string>
<string name="dialog_send_message_log_action_send_chooser_title">Send message log…</string>
<string name="dialog_send_message_log_thanks_for_helping_making_app_better">Thanks a lot for helping us make the app better!</string>
<string name="dialog_send_message_log_error_no_app_to_send_message_found">There are no email clients to send message installed.</string>
</resources>

View File

@ -49,4 +49,8 @@ open class RouterJavaFx : IRouter {
TransferMoneyDialog(presenter, preselectedBankAccount, preselectedValues).show(messages["transfer.money.dialog.title"])
}
override fun showSendMessageLogDialog(presenter: BankingPresenter) {
// TODO
}
}

View File

@ -1,6 +1,7 @@
package net.dankito.banking.ui
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.MessageLogEntry
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
@ -10,6 +11,9 @@ import net.dankito.banking.ui.model.responses.GetTransactionsResponse
interface IBankingClient {
val messageLogWithoutSensitiveData: List<MessageLogEntry>
fun addAccountAsync(callback: (AddAccountResponse) -> Unit)
fun getTransactionsAsync(

View File

@ -20,4 +20,6 @@ interface IRouter {
fun showTransferMoneyDialog(presenter: BankingPresenter, preselectedBankAccount: BankAccount?, preselectedValues: TransferMoneyData?)
fun showSendMessageLogDialog(presenter: BankingPresenter)
}

View File

@ -0,0 +1,16 @@
package net.dankito.banking.ui.model
import java.util.*
open class MessageLogEntry(
val message: String,
val time: Date,
val account: Account
) {
override fun toString(): String {
return message
}
}

View File

@ -5,10 +5,7 @@ import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.IBankingClientCreator
import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.SelectedAccountType
import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
import net.dankito.banking.ui.model.responses.BankingClientResponse
@ -29,6 +26,7 @@ import java.io.File
import java.io.FileOutputStream
import java.math.BigDecimal
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
@ -47,6 +45,8 @@ open class BankingPresenter(
companion object {
protected const val OneDayMillis = 24 * 60 * 60 * 1000
protected val MessageLogEntryDateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS")
private val log = LoggerFactory.getLogger(BankingPresenter::class.java)
}
@ -367,6 +367,17 @@ open class BankingPresenter(
}
open fun getMessageLogForAccounts(accounts: List<Account>): List<String> {
val logEntries = accounts.flatMap {
getClientForAccount(it)?.messageLogWithoutSensitiveData ?: listOf()
}
return logEntries.map { entry ->
MessageLogEntryDateFormat.format(entry.time) + " " + entry.account.bank.bankCode + " " + entry.message
}
}
open fun showAddAccountDialog() {
router.showAddAccountDialog(this)
}
@ -375,6 +386,10 @@ open class BankingPresenter(
router.showTransferMoneyDialog(this, preselectedBankAccount, preselectedValues)
}
open fun showSendMessageLogDialog() {
router.showSendMessageLogDialog(this)
}
protected open fun getClientForAccount(account: Account): IBankingClient? {
return clientsForAccounts.get(account)

View File

@ -4,6 +4,7 @@ import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.MessageLogEntry
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
@ -83,6 +84,10 @@ open class fints4kBankingClient(
})
override val messageLogWithoutSensitiveData: List<MessageLogEntry>
get() = client.messageLogWithoutSensitiveData.map { MessageLogEntry(it.message, it.time, account) }
override fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
client.addAccountAsync { response ->
this.account = mapper.mapAccount(customer, bank)

View File

@ -4,10 +4,7 @@ import net.dankito.banking.model.AccountCredentials
import net.dankito.banking.model.ConnectResult
import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.IBankingClient
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.Bank
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse
@ -66,6 +63,8 @@ open class hbci4jBankingClient(
protected val accountTransactionMapper = AccountTransactionMapper()
override val messageLogWithoutSensitiveData: List<MessageLogEntry> = listOf() // TODO: implement
override fun addAccountAsync(callback: (AddAccountResponse) -> Unit) {
threadPool.runAsync {