Implemented hiding account and disabling automatic account update

This commit is contained in:
dankito 2020-09-28 23:14:36 +02:00
parent 364b818d84
commit 589e1e673a
24 changed files with 163 additions and 59 deletions

View File

@ -66,6 +66,10 @@ open class BankAccount(
override var displayIndex: Int = 0
override var hideAccount = false
override var updateAccountAutomatically = true
override var doNotShowStrikingFetchAllTransactionsView = false

View File

@ -34,6 +34,8 @@ open class BankAccountEntity(
override var haveAllTransactionsBeenRetrieved: Boolean = false,
override var isAccountTypeSupportedByApplication: Boolean = true,
override var displayIndex: Int = 0,
override var hideAccount: Boolean = false,
override var updateAccountAutomatically: Boolean = true,
override var doNotShowStrikingFetchAllTransactionsView: Boolean = false
) : IBankAccount<AccountTransactionEntity> {

View File

@ -46,6 +46,11 @@ open class BankAccountSettingsDialog : SettingsDialogBase() {
edtxtBankAccountName.text = account.displayName
swtchHideAccount.setOnCheckedChangeListener { _, hideAccount -> swtchUpdateAccountAutomatically.isEnabled = hideAccount == false }
swtchHideAccount.isChecked = account.hideAccount
swtchUpdateAccountAutomatically.isChecked = account.updateAccountAutomatically
lvlAccountHolderName.value = account.accountHolderName
lvlAccountIdentifier.value = account.identifier
lvlSubAccountNumber.setValueAndVisibilityIfValueIsSet(account.subAccountNumber)
@ -66,10 +71,15 @@ open class BankAccountSettingsDialog : SettingsDialogBase() {
override val hasUnsavedChanges: Boolean
get() = didChange(edtxtBankAccountName, account.displayName)
|| swtchHideAccount.isChecked != account.hideAccount
|| swtchUpdateAccountAutomatically.isChecked != account.updateAccountAutomatically
override fun saveChanges() {
account.userSetDisplayName = edtxtBankAccountName.text
account.hideAccount = swtchHideAccount.isChecked
account.updateAccountAutomatically = swtchUpdateAccountAutomatically.isChecked
presenter.accountUpdated(account)
}

View File

@ -42,7 +42,7 @@ open class SettingsDialog : SettingsDialogBase() {
protected open fun setupUI(rootView: View) {
rootView.apply {
toolbar.apply {
setupToolbar(this, rootView.context.getString(R.string.dialog_settings_title), false)
setupToolbar(this, rootView.context.getString(R.string.settings), false)
}
val items = createBanksAdapterItems()

View File

@ -190,7 +190,7 @@ class HomeFragment : Fragment() {
}
private fun updateAccountsTransactions() {
presenter.updateSelectedAccountsTransactionsAsync { }
presenter.updateSelectedAccountsTransactionsAsync()
}
private fun handleGetTransactionsResponseOffUiThread(response: GetTransactionsResponse) {
@ -320,20 +320,11 @@ class HomeFragment : Fragment() {
private fun fetchTransactions() {
presenter.selectedAccounts.forEach { account ->
if (account.haveAllTransactionsBeenRetrieved) {
presenter.updateAccountTransactionsAsync(account)
}
else {
presenter.fetchAllAccountTransactionsAsync(account)
}
}
presenter.fetchTransactionsOfSelectedAccounts()
}
private fun fetchAllTransactions() {
accountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account ->
presenter.fetchAllAccountTransactionsAsync(account)
}
presenter.fetchAllTransactionsOfSelectedAccounts()
}
}

View File

@ -105,7 +105,7 @@ open class DrawerView(
,
PrimaryDrawerItem()
.withName(R.string.drawer_menu_show_settings_dialog_title)
.withName(R.string.settings)
.withIcon(R.drawable.ic_baseline_settings_24)
.withIconColor(ContextCompat.getColorStateList(activity, R.color.primaryTextColor_Dark)!!)
.withSelectable(false)
@ -163,7 +163,7 @@ open class DrawerView(
}
private fun createBankAccountsDrawerItems(bank: TypedBankData): List<IDrawerItem<*>> {
return bank.accountsSorted.map { account ->
return bank.visibleAccountsSorted.map { account ->
SecondaryDrawerItem()
.withName(account.displayName)
.withLevel(AccountLevel)

View File

@ -53,6 +53,13 @@
android:layout_height="wrap_content"
>
<net.dankito.banking.ui.android.views.FormSectionTitle
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/form_section_title_margin_bottom_for_subsequent_edit_text"
android:text="@string/settings"
/>
<net.dankito.banking.ui.android.views.FormEditText
android:id="@+id/edtxtBankAccountName"
android:layout_width="match_parent"
@ -61,6 +68,20 @@
android:inputType="text"
/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swtchHideAccount"
style="@style/FormSwitchStyle"
android:checked="false"
android:text="@string/dialog_bank_account_settings_hide_account"
/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swtchUpdateAccountAutomatically"
style="@style/FormSwitchStyle"
android:checked="false"
android:text="@string/dialog_bank_account_settings_update_automatically"
/>
</LinearLayout>
@ -68,6 +89,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/form_section_extra_margin_top"
>
<net.dankito.banking.ui.android.views.FormSectionTitle

View File

@ -14,6 +14,7 @@
<string name="search">Suchen</string>
<string name="add_account">Konto hinzufügen</string>
<string name="settings">Einstellungen</string>
<string name="online_banking_credentials_section_title">Online-Banking Zugangsdaten</string>
<string name="online_banking_credentials_login_name">Login Name</string>
@ -34,7 +35,6 @@
<string name="nav_header_version_label">Version\u0020</string>
<string name="nav_header_desc">Navigationsbereich</string>
<string name="drawer_menu_all_bank_accounts_title">Alle Konten</string>
<string name="drawer_menu_show_settings_dialog_title">Einstellungen</string>
<string name="floating_action_menu_add_account">Konto</string>
<string name="floating_action_menu_transfer_money">Überweisung</string>
@ -105,7 +105,6 @@
<string name="dialog_enter_atc_error_entered_atc_is_not_a_number">ATC muss eine Zahl sein.\n\nDer eingebene ATC Wert \'%s\' kann jedoch nicht in eine Zahl konvertiert werden.</string>
<string name="dialog_settings_title">Einstellungen</string>
<string name="dialog_settings_send_message_log_title">Message Log senden</string>
@ -115,6 +114,8 @@
<string name="dialog_bank_settings_delete_account">Konto löschen</string>
<string name="dialog_bank_account_settings_account_name">Name</string>
<string name="dialog_bank_account_settings_hide_account">Konto ausblenden</string>
<string name="dialog_bank_account_settings_update_automatically">Konto automatisch updaten</string>
<string name="dialog_bank_account_settings_account_data_section_title">Kontodaten</string>
<string name="dialog_bank_account_setting_account_holder_name">Kontoinhaber</string>
<string name="dialog_bank_account_setting_bank_account_identifier">Kontonummer</string>

View File

@ -31,6 +31,9 @@
<dimen name="form_labelled_value_value_text_size">15sp</dimen>
<dimen name="form_labelled_value_value_margin_bottom">4dp</dimen>
<dimen name="form_on_off_value_height">30dp</dimen>
<dimen name="form_on_off_value_label_text_size">15sp</dimen>
<dimen name="fragment_account_transaction_margin_start_and_end">4dp</dimen>
<dimen name="fragment_account_transaction_transactions_summary_height">24dp</dimen>
<dimen name="fragment_account_transaction_fetch_transactions_button_height">40dp</dimen>

View File

@ -14,6 +14,7 @@
<string name="search">Search</string>
<string name="add_account">Add account</string>
<string name="settings">Settings</string>
<string name="online_banking_credentials_section_title">Online banking login data</string>
<string name="online_banking_credentials_login_name">Login name</string>
@ -34,7 +35,6 @@
<string name="nav_header_version_label">Version\u0020</string>
<string name="nav_header_desc">Navigation header</string>
<string name="drawer_menu_all_bank_accounts_title">All accounts</string>
<string name="drawer_menu_show_settings_dialog_title">Settings</string>
<string name="floating_action_menu_add_account">Account</string>
<string name="floating_action_menu_transfer_money">Transfer money</string>
@ -105,7 +105,6 @@
<string name="dialog_enter_atc_error_entered_atc_is_not_a_number">ATC has to be a number.\n\nBut entered ATC value \'%s\' cannot be converted to a number.</string>
<string name="dialog_settings_title">Settings</string>
<string name="dialog_settings_send_message_log_title">Send message log</string>
@ -115,6 +114,8 @@
<string name="dialog_bank_settings_delete_account">Delete account</string>
<string name="dialog_bank_account_settings_account_name">Name</string>
<string name="dialog_bank_account_settings_hide_account">Hide account</string>
<string name="dialog_bank_account_settings_update_automatically">Update account automatically</string>
<string name="dialog_bank_account_settings_account_data_section_title">Account data</string>
<string name="dialog_bank_account_setting_account_holder_name">Account holder name</string>
<string name="dialog_bank_account_setting_bank_account_identifier">Account identifier</string>

View File

@ -78,4 +78,16 @@
<item name="android:src">@drawable/fab_add</item>
</style>
<style name="FormSwitchStyle" parent="Widget.AppCompat.CompoundButton.Switch">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/form_on_off_value_height</item>
<item name="android:textColor">@color/formLabelledValueLabelTextColor</item>
<item name="android:textSize">@dimen/form_on_off_value_label_text_size</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textAlignment">gravity</item>
</style>
</resources>

View File

@ -140,8 +140,7 @@ open class AccountTransactionsControlView(
}
protected open fun updateAccountTransactions(processingIndicatorButton: ProcessingIndicatorButton) {
// TODO: or only update transactions of selected accounts?
presenter.updateAccountsTransactionsAsync {
presenter.updateSelectedAccountsTransactionsAsync {
runLater {
processingIndicatorButton.resetIsProcessing()
}

View File

@ -273,7 +273,7 @@ open class AddAccountDialog(protected val presenter: BankingPresenter) : Window(
val userSelection = dialogService.showDialog(Alert.AlertType.CONFIRMATION, message, null, currentStage, ButtonType.YES, ButtonType.NO)
when (userSelection) {
ButtonType.YES -> presenter.fetchAllAccountTransactionsAsync(response.bank) { }
ButtonType.YES -> presenter.fetchAllAccountTransactionsAsync(response.bank)
else -> { } // nothing to do then, simply close dialog
}

View File

@ -50,6 +50,10 @@ open class BankAccount @JvmOverloads constructor(
override var displayIndex: Int = 0
override var hideAccount = false
override var updateAccountAutomatically = true
override var doNotShowStrikingFetchAllTransactionsView = false

View File

@ -30,6 +30,25 @@ interface IBankAccount<TTransaction: IAccountTransaction> : OrderedDisplayable {
var haveAllTransactionsBeenRetrieved: Boolean
var isAccountTypeSupportedByApplication: Boolean
var userSetDisplayName: String?
/**
* Account will not be visible in UI
*/
var hideAccount: Boolean
/**
* Account is still visible in UI but will not be included in automatic accounts refresh (Kontorundruf) or if multiple accounts get updated.
*
* However it still can be updated if navigated to that single account and call update there.
*/
var updateAccountAutomatically: Boolean
/**
* If there are still older transactions to fetch, that is [haveAllTransactionsBeenRetrieved] is [false], at a striking place,
* e.g. above transactions list or with an overlay, an information will be displayed to fetch all transactions.
*
* However this information can be dismissed by user. Than it still will be visible below transactions list where it's not that well visible to user.
*/
var doNotShowStrikingFetchAllTransactionsView: Boolean

View File

@ -43,6 +43,9 @@ interface IBankData<TAccount: IBankAccount<TAccountTransaction>, TAccountTransac
val accountsSorted: List<TAccount>
get() = accounts.sortedByDisplayIndex()
val visibleAccountsSorted: List<TAccount>
get() = accountsSorted.filter { it.hideAccount == false }
val balance: BigDecimal
get() = accounts.map { it.balance }.sum()

View File

@ -115,7 +115,7 @@ open class BankingPresenter(
readAppSettings()
readPersistedBanks()
updateAccountsTransactionsIfNoTanIsRequiredAsync()
updateAllAccountsTransactionsAsync()
}
// preloadBankList asynchronously; on Android it takes approximately 18 seconds till banks are indexed for first time -> do it as early as possible
@ -252,7 +252,7 @@ open class BankingPresenter(
}
protected open fun deleteAccountOffUiThread(bank: TypedBankData) {
val wasSelected = isSingleSelectedBank(bank) or // either account or one of its bank accounts is currently selected
val wasSelected = isSingleSelectedBank(bank) or // either bank or one of its bank accounts is currently selected
(bank.accounts.firstOrNull { isSingleSelectedAccount(it) } != null)
val client = bankingClientsForBanks.remove(bank)
@ -278,6 +278,28 @@ open class BankingPresenter(
}
/**
* If for an account already all transactions have been fetch, then latest transactions get fetched.
*
* Otherwise all transactions are fetched.
*/
open fun fetchTransactionsOfSelectedAccounts(callback: ((GetTransactionsResponse) -> Unit)? = null) {
selectedAccounts.forEach { account ->
if (account.haveAllTransactionsBeenRetrieved) {
updateAccountTransactionsAsync(account, false, callback)
}
else {
fetchAllAccountTransactionsAsync(account, callback)
}
}
}
open fun fetchAllTransactionsOfSelectedAccounts(callback: ((GetTransactionsResponse) -> Unit)? = null) {
selectedAccountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account ->
fetchAllAccountTransactionsAsync(account, callback)
}
}
open fun fetchAllAccountTransactionsAsync(bank: TypedBankData,
callback: ((GetTransactionsResponse) -> Unit)? = null) {
@ -288,13 +310,13 @@ open class BankingPresenter(
}
}
open fun fetchAllAccountTransactionsAsync(account: TypedBankAccount,
protected open fun fetchAllAccountTransactionsAsync(account: TypedBankAccount,
callback: ((GetTransactionsResponse) -> Unit)? = null) {
fetchAccountTransactionsAsync(account, null, false, callback)
}
open fun fetchAccountTransactionsAsync(account: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false,
protected open fun fetchAccountTransactionsAsync(account: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false,
callback: ((GetTransactionsResponse) -> Unit)? = null) {
getBankingClientForBank(account.bank)?.let { client ->
@ -311,29 +333,25 @@ open class BankingPresenter(
}
}
open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
updateAccountsTransactionsAsync(false, callback)
open fun updateAllAccountsTransactionsAsync(callback: ((GetTransactionsResponse) -> Unit)? = null) {
val accountsToUpdate = allAccounts.filter { it.hideAccount == false && it.updateAccountAutomatically }
updateAccountsTransactionsAsync(accountsToUpdate, true, callback)
}
open fun updateAccountsTransactionsIfNoTanIsRequiredAsync() {
updateAccountsTransactionsAsync(true) { }
}
open fun updateSelectedAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
updateAccountsTransactionsAsync(selectedAccounts, false, callback)
}
protected open fun updateAccountsTransactionsAsync(abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
bankingClientsForBanks.keys.forEach { account ->
account.accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
updateAccountTransactionsAsync(account, abortIfTanIsRequired, callback)
}
}
open fun updateSelectedAccountsTransactionsAsync(callback: ((GetTransactionsResponse) -> Unit)? = null) {
var accountsToUpdate = selectedAccounts.filter { it.updateAccountAutomatically }
if (accountsToUpdate.isEmpty() && (selectedAccountType == SelectedAccountType.SingleAccount
|| (selectedAccountType == SelectedAccountType.SingleBank && selectedAccounts.size == 1))) {
accountsToUpdate = selectedAccounts
}
updateAccountsTransactionsAsync(accountsToUpdate, false, callback)
}
protected open fun updateAccountsTransactionsAsync(accounts: List<TypedBankAccount>, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
protected open fun updateAccountsTransactionsAsync(accounts: List<TypedBankAccount>, abortIfTanIsRequired: Boolean = false, callback: ((GetTransactionsResponse) -> Unit)? = null) {
accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
updateAccountTransactionsAsync(account, abortIfTanIsRequired, callback)
@ -733,7 +751,7 @@ open class BankingPresenter(
}
protected open fun setSelectedAccounts(accounts: List<TypedBankAccount>) {
this._selectedAccounts = ArrayList(accounts) // make a copy
this._selectedAccounts = ArrayList(accounts.filter { it.hideAccount == false }) // make a copy
callSelectedAccountsChangedListeners(_selectedAccounts)
}

View File

@ -44,6 +44,7 @@
<attribute name="displayIndex" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="doNotShowStrikingFetchAllTransactionsView" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="haveAllTransactionsBeenRetrieved" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="hideAccount" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="iban" optional="YES" attributeType="String"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="isAccountTypeSupportedByApplication" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
@ -56,6 +57,7 @@
<attribute name="supportsRetrievingBalance" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="supportsTransferringMoney" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String"/>
<attribute name="updateAccountAutomatically" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="userSetDisplayName" optional="YES" attributeType="String"/>
<relationship name="bank" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistedBankData" inverseName="accounts" inverseEntity="PersistedBankData"/>
<relationship name="transactions" toMany="YES" deletionRule="Cascade" destinationEntity="PersistedAccountTransaction" inverseName="account" inverseEntity="PersistedAccountTransaction"/>
@ -91,7 +93,7 @@
</entity>
<elements>
<element name="PersistedAccountTransaction" positionX="-36" positionY="45" width="128" height="553"/>
<element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="28"/>
<element name="PersistedBankAccount" positionX="-54" positionY="63" width="128" height="403"/>
<element name="PersistedBankData" positionX="-63" positionY="-18" width="128" height="283"/>
<element name="PersistedTanMedium" positionX="-45" positionY="144" width="128" height="28"/>
<element name="PersistedTanMethod" positionX="-54" positionY="135" width="128" height="118"/>

View File

@ -178,6 +178,9 @@ Unfortunately, Bankmeister cannot know whether a bank charges for real-time tran
/* BankAccountSettingsDialog */
"Account holder name" = "Account holder name";
"Hide bank account" = "Hide account";
"Update bank account automatically" = "Update account automatically";
"Bank account identifier" = "Account identifier";
"Sub account number" = "Sub account number";
"Bank account type" = "Type";

View File

@ -179,6 +179,9 @@ Ob eine Bank Gebühren für Echtzeitüberweisungen erhebt, kann Bankmeister leid
/* BankAccountSettingsDialog */
"Account holder name" = "Kontoinhaber";
"Hide bank account" = "Konto ausblenden";
"Update bank account automatically" = "Konto automatisch updaten";
"Bank account identifier" = "Kontonummer";
"Sub account number" = "Unterkontenmerkmal";
"Bank account type" = "Typ";

View File

@ -218,20 +218,11 @@ struct AccountTransactionsDialog: View {
}
private func fetchTransactions() {
for account in presenter.selectedAccounts {
if account.haveAllTransactionsBeenRetrieved {
presenter.updateAccountTransactionsAsync(account: account, abortIfTanIsRequired: false, callback: self.handleGetTransactionsResult)
}
else {
presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
}
}
presenter.fetchTransactionsOfSelectedAccounts(callback: self.handleGetTransactionsResult)
}
private func fetchAllTransactions() {
accountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account in
presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
}
presenter.fetchAllTransactionsOfSelectedAccounts(callback: self.handleGetTransactionsResult)
}
private func handleGetTransactionsResult(_ response: GetTransactionsResponse) {

View File

@ -37,7 +37,7 @@ struct AccountsDialog: View {
.systemGroupedBackground()
.showNavigationBarTitle("Accounts")
.navigationBarItems(leading: data.hasAtLeastOneAccountBeenAdded == false ? nil : UpdateButton { _, executingDone in
self.presenter.updateAccountsTransactionsAsync { _ in executingDone() }
self.presenter.updateAllAccountsTransactionsAsync { _ in executingDone() }
})
}

View File

@ -13,11 +13,17 @@ struct BankAccountSettingsDialog: View {
@State private var displayName: String
@State private var hideAccount: Bool
@State private var updateAccountAutomatically: Bool
@State private var unsavedChangesMessage: Message? = nil
private var hasUnsavedData: Bool {
return account.displayName != displayName
|| account.hideAccount != hideAccount
|| account.updateAccountAutomatically != updateAccountAutomatically
}
@ -25,6 +31,8 @@ struct BankAccountSettingsDialog: View {
self.account = account
_displayName = State(initialValue: account.displayName)
_hideAccount = State(initialValue: account.hideAccount)
_updateAccountAutomatically = State(initialValue: account.updateAccountAutomatically)
}
@ -32,6 +40,11 @@ struct BankAccountSettingsDialog: View {
Form {
Section {
LabelledUIKitTextField(label: "Name", text: $displayName, autocapitalizationType: .none)
Toggle("Hide bank account", isOn: $hideAccount)
Toggle("Update bank account automatically", isOn: $updateAccountAutomatically)
.disabled(hideAccount)
}
Section {
@ -80,6 +93,9 @@ struct BankAccountSettingsDialog: View {
if hasUnsavedData {
account.userSetDisplayName = displayName
account.hideAccount = hideAccount
account.updateAccountAutomatically = updateAccountAutomatically
presenter.accountUpdated(account: account)
}

View File

@ -48,7 +48,7 @@ struct BankListItem : View {
// if a constant id like \.technicalId is provided, list doesn't get updated on changes e.g. when balance changes
ForEach(bank.accountsSorted, id: \.randomId) { account in
ForEach(bank.visibleAccountsSorted, id: \.randomId) { account in
BankAccountListItem(account: account)
}
.padding(.leading, Styles.AccountsIconWidth + Styles.DefaultSpaceBetweenIconAndText)