Implemented deleting an account (TODO: implement dialog to edit account e.g. set display name etc.)

This commit is contained in:
dankito 2020-04-22 22:22:58 +02:00
parent 2070eb5dcd
commit 88ae4cb045
27 changed files with 272 additions and 5 deletions

View File

@ -7,6 +7,8 @@ interface IBankingPersistence {
fun saveOrUpdateAccount(account: Account, allAccounts: List<Account>) fun saveOrUpdateAccount(account: Account, allAccounts: List<Account>)
fun deleteAccount(account: Account, allAccounts: List<Account>)
fun readPersistedAccounts(): List<Account> fun readPersistedAccounts(): List<Account>
} }

View File

@ -158,6 +158,21 @@ open class BankingPresenter(
} }
} }
open fun deleteAccount(account: Account) {
val wasSelected = isSingleSelectedAccount(account) or // either account or one of its bank accounts is currently selected
(account.bankAccounts.firstOrNull { isSingleSelectedBankAccount(it) } != null)
clientsForAccounts.remove(account)
persister.deleteAccount(account, accounts)
callAccountsChangedListeners()
if (wasSelected) {
selectedAllBankAccounts()
}
}
open fun getAccountTransactionsAsync(account: Account, open fun getAccountTransactionsAsync(account: Account,
callback: (GetTransactionsResponse) -> Unit) { callback: (GetTransactionsResponse) -> Unit) {

View File

@ -3,13 +3,14 @@ package net.dankito.banking.fints4java.android.ui.extensions
import android.content.Context import android.content.Context
import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.materialdrawer.iconics.withIcon import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.AbstractBadgeableDrawerItem import com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem
import com.mikepenz.materialdrawer.model.BaseViewHolder
import com.mikepenz.materialdrawer.model.interfaces.withIconColor import com.mikepenz.materialdrawer.model.interfaces.withIconColor
import net.dankito.utils.android.extensions.createColorStateList import net.dankito.utils.android.extensions.createColorStateList
fun <Item : AbstractBadgeableDrawerItem<Item>> AbstractBadgeableDrawerItem<Item>.withIcon( fun <T, VH : BaseViewHolder> BaseDescribeableDrawerItem<T, VH>.withIcon(context: Context, icon: IIcon, iconColorId: Int)
context: Context, icon: IIcon, iconColorId: Int): AbstractBadgeableDrawerItem<Item> { : BaseDescribeableDrawerItem<T, VH> {
withIcon(icon) withIcon(icon)

View File

@ -0,0 +1,6 @@
package net.dankito.banking.fints4java.android.ui.views
class AccountDrawerItem : SecondaryIconDrawerItem<AccountDrawerItem>() {
}

View File

@ -2,7 +2,10 @@ package net.dankito.banking.fints4java.android.ui.views
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
@ -112,10 +115,15 @@ open class DrawerView(
}.flatten() }.flatten()
} }
private fun createAccountDrawerItem(account: Account): PrimaryDrawerItem { private fun createAccountDrawerItem(account: Account): IDrawerItem<*> {
return PrimaryDrawerItem()
return AccountDrawerItem()
.withName(account.displayName) .withName(account.displayName)
.withLevel(AccountLevel) .withLevel(AccountLevel)
// .withSecondaryIcon(GoogleMaterial.Icon.gmd_settings) // used when editing account is implemented
.withSecondaryIcon(GoogleMaterial.Icon.gmd_delete)
.withSecondaryIconColor(activity, R.color.primaryTextColor_Dark)
.withOnSecondaryIconClickedListener { closeDrawerAndEditAccount(account) }
.withIcon(activity, FontAwesome.Icon.faw_piggy_bank, R.color.primaryTextColor_Dark) .withIcon(activity, FontAwesome.Icon.faw_piggy_bank, R.color.primaryTextColor_Dark)
.withSelected(presenter.isSingleSelectedAccount(account)) .withSelected(presenter.isSingleSelectedAccount(account))
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(account) } } .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(account) } }
@ -137,6 +145,25 @@ open class DrawerView(
return false return false
} }
private fun closeDrawerAndEditAccount(account: Account) {
closeDrawer()
editAccount(account)
}
private fun editAccount(account: Account) {
// TODO: implement editing account (e.g. displayed name etc.)
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.dialog_edit_account_ask_should_account_be_deleted, account.displayName))
.setPositiveButton(R.string.delete) { dialog, _ ->
dialog.dismiss()
presenter.deleteAccount(account)
}
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.show()
}
private fun showAppVersion(navigationHeaderView: View?) { private fun showAppVersion(navigationHeaderView: View?) {
try { try {
val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0) val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
@ -147,4 +174,9 @@ open class DrawerView(
} }
} }
private fun closeDrawer() {
val drawerLayout = activity.findViewById<DrawerLayout>(R.id.drawer_layout)
drawerLayout.closeDrawer(GravityCompat.START)
}
} }

View File

@ -0,0 +1,122 @@
package net.dankito.banking.fints4java.android.ui.views
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import android.view.View
import android.widget.ImageView
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.iconics.IconicsImageHolder
import com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem
import com.mikepenz.materialdrawer.model.BaseViewHolder
import net.dankito.banking.fints4java.android.R
import net.dankito.utils.android.extensions.createColorStateList
open class SecondaryIconDrawerItem<Item : SecondaryIconDrawerItem<Item>> : BaseDescribeableDrawerItem<Item, SecondaryIconDrawerItem.ViewHolder>() {
var secondaryIcon: ImageHolder? = null
var secondaryIconColor: ColorStateList? = null
var onSecondaryIconClicked: (() -> Unit)? = null
override val type: Int
get() = R.id.material_drawer_item_secondary_icon
override val layoutRes: Int
@LayoutRes
get() = R.layout.material_drawer_item_secondary_icon
override fun bindView(holder: ViewHolder, payloads: List<Any>) {
super.bindView(holder, payloads)
bindViewHelper(holder)
if (secondaryIcon == null) {
holder.btnSecondaryIcon.visibility = View.GONE
}
else {
val context = holder.itemView.context
val secondaryIconColor = this.secondaryIconColor ?: getIconColor(context)
val secondaryIcon = ImageHolder.decideIcon(secondaryIcon, context, secondaryIconColor, isIconTinted, 1)
holder.btnSecondaryIcon.setImageDrawable(secondaryIcon)
holder.btnSecondaryIcon.setOnClickListener { onSecondaryIconClicked?.invoke() }
holder.btnSecondaryIcon.visibility = View.VISIBLE
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, holder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
open class ViewHolder(view: View) : BaseViewHolder(view) {
internal val btnSecondaryIcon = view.findViewById<ImageView>(R.id.btnSecondaryIcon)
}
open fun withSecondaryIconColor(iconColor: ColorStateList): Item {
this.secondaryIconColor = iconColor
return this as Item
}
open fun withSecondaryIconColor(context: Context, iconColorResId: Int): Item {
return withSecondaryIconColor(context.createColorStateList(iconColorResId))
}
open fun withSecondaryIcon(icon: Drawable?): Item {
this.secondaryIcon = ImageHolder(icon)
return this as Item
}
open fun withSecondaryIcon(icon: Bitmap): Item {
this.secondaryIcon = ImageHolder(icon)
return this as Item
}
open fun withSecondaryIcon(@DrawableRes imageRes: Int): Item {
this.secondaryIcon = ImageHolder(imageRes)
return this as Item
}
open fun withSecondaryIcon(url: String): Item {
this.secondaryIcon = ImageHolder(url)
return this as Item
}
open fun withSecondaryIcon(uri: Uri): Item {
this.secondaryIcon = ImageHolder(uri)
return this as Item
}
open fun withSecondaryIcon(icon: ImageHolder?): Item {
this.secondaryIcon = icon
return this as Item
}
open fun withSecondaryIcon(icon: IIcon): Item {
this.secondaryIcon = IconicsImageHolder(icon)
return this as Item
}
open fun withOnSecondaryIconClickedListener(clickListener: () -> Unit): Item {
this.onSecondaryIconClicked = clickListener
return this as Item
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/material_drawer_item_primary"
android:orientation="horizontal"
android:paddingEnd="@dimen/material_drawer_vertical_padding"
android:paddingLeft="@dimen/material_drawer_vertical_padding"
android:paddingRight="@dimen/material_drawer_vertical_padding"
android:paddingStart="@dimen/material_drawer_vertical_padding">
<ImageView
android:id="@+id/material_drawer_icon"
android:layout_width="@dimen/material_drawer_item_primary_icon"
android:layout_height="@dimen/material_drawer_item_primary"
android:layout_gravity="center_vertical"
android:paddingBottom="@dimen/material_drawer_item_primary_icon_padding"
android:paddingEnd="@dimen/material_drawer_item_primary_icon_padding_right"
android:paddingLeft="0dp"
android:paddingRight="@dimen/material_drawer_item_primary_icon_padding_right"
android:paddingStart="0dp"
android:paddingTop="@dimen/material_drawer_item_primary_icon_padding" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:orientation="vertical">
<TextView
android:id="@+id/material_drawer_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textDirection="anyRtl"
android:textSize="@dimen/material_drawer_item_primary_text"
tools:text="Some drawer text" />
<TextView
android:id="@+id/material_drawer_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textDirection="anyRtl"
android:textSize="@dimen/material_drawer_item_primary_description"
tools:text="Some drawer text" />
</LinearLayout>
<ImageButton
android:id="@+id/btnSecondaryIcon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:gravity="center"
android:paddingEnd="0dp"
android:paddingLeft="@dimen/material_drawer_vertical_padding"
android:paddingRight="0dp"
android:paddingStart="@dimen/material_drawer_vertical_padding"
android:scaleType="fitCenter" />
</LinearLayout>

View File

@ -7,6 +7,7 @@
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nein</string> <string name="no">Nein</string>
<string name="fetch">Abrufen</string> <string name="fetch">Abrufen</string>
<string name="delete">Löschen</string>
<string name="search">Suchen</string> <string name="search">Suchen</string>
@ -84,4 +85,7 @@
<string name="dialog_enter_atc_atc_label">ATC:</string> <string name="dialog_enter_atc_atc_label">ATC:</string>
<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_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_edit_account_ask_should_account_be_deleted">Möchten Sie das Konto \'%s\' wirklich löschen?
\n\nDies kann nicht rückgängig gemacht werden und die hierzu gespeicherten Daten gehen unwiederbringlich verloren.</string>
</resources> </resources>

View File

@ -11,6 +11,8 @@
<color name="backgroundColor_Dark">#303030</color> <color name="backgroundColor_Dark">#303030</color>
<color name="backgroundColor_Light">#FFFFFF</color> <color name="backgroundColor_Light">#FFFFFF</color>
<color name="drawerMenuItemPrimaryColor">@color/primaryTextColor_Dark</color>
<color name="positiveAmount">#43A047</color> <color name="positiveAmount">#43A047</color>
<color name="negativeAmount">#E53935</color> <color name="negativeAmount">#E53935</color>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="material_drawer_item_secondary_icon" type="id" />
</resources>

View File

@ -7,6 +7,7 @@
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="no">No</string> <string name="no">No</string>
<string name="fetch">Fetch</string> <string name="fetch">Fetch</string>
<string name="delete">Delete</string>
<string name="search">Search</string> <string name="search">Search</string>
@ -84,4 +85,7 @@
<string name="dialog_enter_atc_atc_label">ATC:</string> <string name="dialog_enter_atc_atc_label">ATC:</string>
<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_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_edit_account_ask_should_account_be_deleted">Really delete account \'%s\'?
\n\nThis cannot be undone and data will be lost.</string>
</resources> </resources>

View File

@ -21,6 +21,10 @@ open class BankingPersistenceJson(
serializer.serializeObject(allAccounts, jsonFile) serializer.serializeObject(allAccounts, jsonFile)
} }
override fun deleteAccount(account: Account, allAccounts: List<Account>) {
serializer.serializeObject(allAccounts, jsonFile)
}
override fun readPersistedAccounts(): List<Account> { override fun readPersistedAccounts(): List<Account> {
return serializer.deserializeListOr(jsonFile, Account::class.java, listOf()) return serializer.deserializeListOr(jsonFile, Account::class.java, listOf())
} }