diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt index 27d99008..af565663 100644 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt +++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/RoomBankingPersistence.kt @@ -13,8 +13,7 @@ import net.dankito.banking.ui.model.TypedBankData import net.dankito.banking.ui.model.settings.AppSettings import net.dankito.banking.ui.model.tan.MobilePhoneTanMedium import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium -import net.dankito.banking.util.persistence.doSaveUrlToFile -import net.dankito.utils.multiplatform.File +import net.dankito.banking.util.persistence.downloadIcon import net.sqlcipher.database.SQLiteDatabase import net.sqlcipher.database.SupportFactory @@ -197,8 +196,13 @@ open class RoomBankingPersistence(applicationContext: Context, password: String? } - override fun saveUrlToFile(url: String, file: File) { - doSaveUrlToFile(url, file) + override fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) { + val iconData = downloadIcon(iconUrl) + bank.iconData = iconData + + (bank as? Bank)?.let { + db.bankDao().saveOrUpdate(it) + } } diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt index 246e3386..468ce9e3 100644 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt +++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/Bank.kt @@ -21,7 +21,7 @@ open class Bank( override var customerName: String, override var userId: String = userName, - override var iconUrl: String? = null, + override var iconData: ByteArray? = null, @Ignore override var accounts: List = listOf(), diff --git a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt index 0e64b0c7..d61eba78 100644 --- a/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt +++ b/persistence/database/RoomBankingPersistence/src/main/java/net/dankito/banking/persistence/model/RoomModelCreator.kt @@ -12,10 +12,12 @@ import net.dankito.utils.multiplatform.Date open class RoomModelCreator : IModelCreator { - override fun createBank(bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, - bic: String, customerName: String, userId: String, iconUrl: String?): TypedBankData { + override fun createBank( + bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, + bic: String, customerName: String, userId: String, iconData: ByteArray? + ): TypedBankData { - return Bank(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) + return Bank(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconData) } override fun createAccount(bank: TypedBankData, productName: String?, identifier: String): TypedBankAccount { diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt index ea9c5f5a..c7b8d8b3 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt @@ -5,7 +5,7 @@ import net.dankito.banking.ui.model.* import net.dankito.banking.ui.model.settings.AppSettings import net.dankito.utils.multiplatform.File import net.dankito.banking.util.ISerializer -import net.dankito.banking.util.persistence.doSaveUrlToFile +import net.dankito.banking.util.persistence.downloadIcon open class BankingPersistenceJson( @@ -24,6 +24,8 @@ open class BankingPersistenceJson( protected val appSettingsJsonFile: File + protected var readBanks: List? = null + init { databaseFolder.mkdirs() @@ -42,7 +44,11 @@ open class BankingPersistenceJson( } override fun readPersistedBanks(): List { - return serializer.deserializeListOr(banksJsonFile, BankDataEntity::class).map { it as TypedBankData } + val banks = serializer.deserializeListOr(banksJsonFile, BankDataEntity::class).map { it as TypedBankData } + + this.readBanks = banks + + return banks } @@ -66,8 +72,12 @@ open class BankingPersistenceJson( } - override fun saveUrlToFile(url: String, file: File) { - doSaveUrlToFile(url, file) + override fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) { + bank.iconData = downloadIcon(iconUrl) + + readBanks?.let { + saveOrUpdateBank(bank, it) + } } } \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt index 8004f098..cc010de6 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt @@ -13,10 +13,12 @@ import net.dankito.utils.multiplatform.Date open class EntitiesModelCreator : IModelCreator { - override fun createBank(bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, - customerName: String, userId: String, iconUrl: String?): TypedBankData { + override fun createBank( + bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, + customerName: String, userId: String, iconData: ByteArray? + ): TypedBankData { - return BankDataEntity(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) as TypedBankData + return BankDataEntity(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconData) as TypedBankData } diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt index b28a6938..224add0b 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankDataEntity.kt @@ -18,7 +18,7 @@ open class BankDataEntity( override var bic: String, override var customerName: String, override var userId: String = userName, - override var iconUrl: String? = null, + override var iconData: ByteArray? = null, override var accounts: List = listOf(), override var supportedTanMethods: List = listOf(), override var selectedTanMethod: TanMethod? = null, diff --git a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt index 5b78c323..dcb92631 100644 --- a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt +++ b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt @@ -34,8 +34,6 @@ class BankingPersistenceJsonTest { const val UserId = CustomerId - const val IconUrl = "http://i-do-not-exist.fail/favicon.ico" - val NowMillis = System.currentTimeMillis() val TwoYearsAgoMillis = NowMillis - (2 * 365 * 24 * 60 * 60 * 1000L) @@ -116,7 +114,7 @@ class BankingPersistenceJsonTest { private fun createBank(countAccounts: Int = 0, customerId: String = CustomerId): BankDataEntity { - val result = BankDataEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl) + val result = BankDataEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, null) result.accounts = createAccounts(countAccounts, result) @@ -180,7 +178,7 @@ class BankingPersistenceJsonTest { assertThat(deserializedBank.bic).isEqualTo(bank.bic) assertThat(deserializedBank.customerName).isEqualTo(bank.customerName) assertThat(deserializedBank.userId).isEqualTo(bank.userId) - assertThat(deserializedBank.iconUrl).isEqualTo(bank.iconUrl) + assertThat(deserializedBank.iconData).isEqualTo(bank.iconData) assertAccountsEqual(deserializedBank.accounts, bank.accounts) } diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt index 58547283..7379f4d9 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt @@ -1,10 +1,10 @@ package net.dankito.banking.ui.android.adapter -import android.net.Uri import android.view.ContextMenu import android.view.View import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.adapter.viewholder.AccountTransactionViewHolder +import net.dankito.banking.ui.android.extensions.setIcon import net.dankito.banking.ui.android.extensions.showAmount import net.dankito.banking.ui.model.IAccountTransaction import net.dankito.banking.ui.presenter.BankingPresenter @@ -39,10 +39,10 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter) viewHolder.txtvwAmount.showAmount(presenter, item.amount, item.currency) - val iconUrl = item.account.bank.iconUrl - if (iconUrl != null && presenter.areAllAccountSelected) { + if (presenter.areAllAccountSelected) { + viewHolder.imgvwBankIcon.setIcon(item.account.bank) + // TODO: if bank icon isn't set: Show default icon? show at least an empty space to that amount and date don't shift up causing an inconsistent view? viewHolder.imgvwBankIcon.visibility = View.VISIBLE - viewHolder.imgvwBankIcon.setImageURI(Uri.parse(iconUrl)) } else { viewHolder.imgvwBankIcon.visibility = View.GONE diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt new file mode 100644 index 00000000..8692542b --- /dev/null +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ByteArrayExtensions.kt @@ -0,0 +1,16 @@ +package net.dankito.banking.ui.android.extensions + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable + + +fun ByteArray.toBitmap(): Bitmap { + return BitmapFactory.decodeByteArray(this, 0, this.size) +} + +fun ByteArray.toDrawable(resources: Resources): Drawable { + return BitmapDrawable(resources, this.toBitmap()) +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt index 07ce4090..24bbf9a0 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt @@ -1,6 +1,5 @@ package net.dankito.banking.ui.android.extensions -import android.net.Uri import android.view.View import android.widget.ImageView import net.dankito.banking.ui.model.IBankData @@ -8,10 +7,21 @@ import net.dankito.banking.ui.model.IBankData fun ImageView.setIcon(bank: IBankData<*, *>) { try { - val iconUrl = bank.iconUrl - this.visibility = if (iconUrl == null) View.GONE else View.VISIBLE - this.setImageURI(Uri.parse(iconUrl)) + val iconData = bank.iconData + + if (iconData != null) { + this.visibility = View.VISIBLE + this.setImageFromBytes(iconData) + } + else { + this.visibility = View.GONE + this.setImageURI(null) + } } catch (e: Exception) { this.visibility = View.GONE } +} + +fun ImageView.setImageFromBytes(data: ByteArray) { + this.setImageBitmap(data.toBitmap()) } \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt index 3f84da20..13e80abb 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt @@ -21,6 +21,7 @@ import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.dialogs.settings.BankSettingsDialog import net.dankito.banking.ui.android.dialogs.settings.SettingsDialog +import net.dankito.banking.ui.android.extensions.toDrawable import net.dankito.banking.ui.android.extensions.withIcon import net.dankito.banking.ui.model.TypedBankData import net.dankito.banking.ui.presenter.BankingPresenter @@ -87,6 +88,7 @@ open class DrawerView( .withIdentifier(AllAccountsId) .withLevel(BankLevel) .withSelected(true) + // TODO: set default account icon (here and below if bank.iconUrl isn't set .withIcon(activity, GoogleMaterial.Icon.gmd_account_balance, R.color.primaryTextColor_Dark) .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAllAccounts() } } , @@ -150,12 +152,13 @@ open class DrawerView( .withSecondaryIcon(R.drawable.ic_baseline_settings_24) .withSecondaryIconColor(activity, R.color.primaryTextColor_Dark) .withOnSecondaryIconClickedListener { closeDrawerAndEditAccount(bank) } - .withIcon(bank.iconUrl ?: "") + .withIcon(bank.iconData?.toDrawable(activity.resources)) .withSelected(presenter.isSingleSelectedBank(bank)) .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedBank(bank) } } - if (bank.iconUrl == null) { - accountItem.withIcon(activity, FontAwesome.Icon.faw_piggy_bank, R.color.primaryTextColor_Dark) + if (bank.iconData == null) { + // TODO: use better default icon + accountItem.withIcon(activity, GoogleMaterial.Icon.gmd_account_balance, R.color.primaryTextColor_Dark) } diff --git a/ui/BankingAndroidApp/src/main/res/layout/dialog_transfer_money.xml b/ui/BankingAndroidApp/src/main/res/layout/dialog_transfer_money.xml index c42755c2..4154afe6 100644 --- a/ui/BankingAndroidApp/src/main/res/layout/dialog_transfer_money.xml +++ b/ui/BankingAndroidApp/src/main/res/layout/dialog_transfer_money.xml @@ -148,7 +148,7 @@ android:ellipsize="end" android:gravity="center_vertical" android:textAlignment="gravity" - android:textSize="@dimen/view_real_time_transfer_info_text_size" + android:textSize="@dimen/view_info_popup_text_size" android:text="@string/dialog_transfer_money_real_time_transfer" /> diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt index e87d1b2c..c0416ffb 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt @@ -19,6 +19,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.util.InputValidator import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.search.TransactionParty +import net.dankito.banking.ui.javafx.extensions.createBankIconImageView import net.dankito.utils.multiplatform.toBigDecimal import net.dankito.banking.ui.javafx.extensions.focusNextControl import net.dankito.utils.javafx.ui.controls.AutoCompletionSearchTextField @@ -115,15 +116,8 @@ open class TransferMoneyDialog @JvmOverloads constructor( cellFormat { text = it.displayName - it.bank.iconUrl?.let { iconUrl -> - graphic = ImageView(iconUrl)?.apply { - this.fitHeight = BankIconSize - this.fitWidth = BankIconSize - this.isPreserveRatio = true - } - contentDisplay = ContentDisplay.LEFT - } - ?: run { contentDisplay = ContentDisplay.TEXT_ONLY } + graphic = it.bank.createBankIconImageView(BankIconSize) + contentDisplay = ContentDisplay.LEFT } } diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/BankDataExtensions.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/BankDataExtensions.kt new file mode 100644 index 00000000..9128e4ad --- /dev/null +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/BankDataExtensions.kt @@ -0,0 +1,17 @@ +package net.dankito.banking.ui.javafx.extensions + +import javafx.scene.image.ImageView +import net.dankito.banking.ui.model.IBankData + + +fun IBankData<*, *>.createBankIconImageView(iconSize: Double): ImageView { + // TODO: set default icon if iconData is null + val image = iconData?.toImage() + val iconImageView = ImageView(image) + + iconImageView.fitHeight = iconSize + iconImageView.fitWidth = iconSize + iconImageView.isPreserveRatio = true + + return iconImageView +} \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/ByteArrayExtensions.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/ByteArrayExtensions.kt new file mode 100644 index 00000000..9c4da5f9 --- /dev/null +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/extensions/ByteArrayExtensions.kt @@ -0,0 +1,9 @@ +package net.dankito.banking.ui.javafx.extensions + +import javafx.scene.image.Image +import java.io.ByteArrayInputStream + + +fun ByteArray.toImage(): Image { + return Image(ByteArrayInputStream(this)) +} \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt index 6b4a6918..9db27d82 100755 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt @@ -1,7 +1,7 @@ package net.dankito.banking.ui.javafx.model import javafx.scene.Node -import javafx.scene.image.ImageView +import net.dankito.banking.ui.javafx.extensions.createBankIconImageView import net.dankito.banking.ui.model.TypedBankData @@ -23,18 +23,7 @@ open class AccountsAccountTreeItem(val bank: TypedBankData) : AccountsTreeItemBa } protected open fun createIconImageView(): Node? { - bank.iconUrl?.let { - val iconImageView = ImageView(it) - - iconImageView.fitHeight = IconSize - iconImageView.fitWidth = IconSize - iconImageView.isPreserveRatio = true - - return iconImageView - } - - // TODO: otherwise set default icon - return null + return bank.createBankIconImageView(IconSize) } } \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt index 1eeb4352..d2998789 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt @@ -22,6 +22,6 @@ interface IBankingPersistence { fun readPersistedAppSettings(): AppSettings? - fun saveUrlToFile(url: String, file: File) + fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) } \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt index 12e05f3e..db94a4bb 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt @@ -2,7 +2,6 @@ package net.dankito.banking.persistence import net.dankito.banking.ui.model.* import net.dankito.banking.ui.model.settings.AppSettings -import net.dankito.utils.multiplatform.File open class NoOpBankingPersistence : IBankingPersistence { @@ -34,7 +33,7 @@ open class NoOpBankingPersistence : IBankingPersistence { } - override fun saveUrlToFile(url: String, file: File) { + override fun saveBankIcon(bank: TypedBankData, iconUrl: String, fileExtension: String?) { } diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankData.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankData.kt index 43f75907..ac4fd054 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankData.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankData.kt @@ -14,7 +14,7 @@ open class BankData( override var bic: String, override var customerName: String, override var userId: String = userName, - override var iconUrl: String? = null, + override var iconData: ByteArray? = null, override var accounts: List = listOf() ) : TypedBankData { diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankData.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankData.kt index b842331b..44bc29ac 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankData.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankData.kt @@ -23,7 +23,7 @@ interface IBankData, TAccountTransac var customerName: String var userId: String - var iconUrl: String? + var iconData: ByteArray? var accounts: List diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt index 7f9c9602..4b64f210 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt @@ -7,10 +7,12 @@ import net.dankito.utils.multiplatform.Date open class DefaultModelCreator : IModelCreator { - override fun createBank(bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, - customerName: String, userId: String, iconUrl: String?): TypedBankData { + override fun createBank( + bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, + customerName: String, userId: String, iconData: ByteArray? + ): TypedBankData { - return BankData(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) + return BankData(bankCode, userName, password, finTsServerAddress, bankName, bic, customerName, userId, iconData) } diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt index 58252e1d..c883f1f8 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt @@ -9,7 +9,7 @@ import net.dankito.utils.multiplatform.Date interface IModelCreator { fun createBank(bankCode: String, userName: String, password: String, finTsServerAddress: String, bankName: String, bic: String, - customerName: String = "", userId: String = userName, iconUrl: String? = null): TypedBankData + customerName: String = "", userId: String = userName, iconData: ByteArray? = null): TypedBankData fun createAccount(bank: TypedBankData, productName: String?, identifier: String) : TypedBankAccount diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt index e393322a..3db9f488 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt @@ -209,33 +209,15 @@ open class BankingPresenter( } protected open fun handleFindIconForBankResult(bank: TypedBankData, bankIconUrl: String) { - val bankIconFile = saveBankIconToDisk(bank, bankIconUrl) - - var iconFilePath = bankIconFile.getAbsolutePath() - - if (iconFilePath.startsWith("file://", true) == false) { - iconFilePath = "file://" + iconFilePath // without 'file://' Android will not find it + try { + persister.saveBankIcon(bank, bankIconUrl, getIconFileExtension(bankIconUrl)) + } catch (e: Exception) { + log.error(e) { "Could not download bank icon from url $bankIconUrl" } } - bank.iconUrl = iconFilePath - - persistBankOffUiThread(bank) - callBanksChangedListeners() } - protected open fun saveBankIconToDisk(bank: TypedBankData, bankIconUrl: String): File { - val bankIconsDir = File(dataFolder, "bank_icons") - bankIconsDir.mkdirs() - - val extension = getIconFileExtension(bankIconUrl) - val bankIconFile = File(bankIconsDir, bank.bankCode + if (extension != null) (".$extension") else "") - - persister.saveUrlToFile(bankIconUrl, bankIconFile) - - return bankIconFile - } - protected open fun getIconFileExtension(bankIconUrl: String): String? { try { var iconFilename = File(bankIconUrl).filename diff --git a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt index 2529c6d2..541100cd 100644 --- a/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt +++ b/ui/BankingUiCommon/src/jvmMain/kotlin/net/dankito/banking/util/persistence/IBankingPersistenceExtensions.kt @@ -12,4 +12,10 @@ fun IBankingPersistence.doSaveUrlToFile(url: String, file: File) { iconInputStream.copyTo(fileOutputStream) } } +} + +fun IBankingPersistence.downloadIcon(url: String): ByteArray { + URL(url).openConnection().getInputStream().buffered().use { iconInputStream -> + return iconInputStream.readBytes() + } } \ No newline at end of file diff --git a/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/UiExtensions.kt b/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/UiExtensions.kt new file mode 100644 index 00000000..c69e441d --- /dev/null +++ b/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/UiExtensions.kt @@ -0,0 +1,30 @@ +package net.dankito.banking + +import kotlinx.cinterop.* +import platform.Foundation.* + + +/** + * Swift doesn't see the ByteArray- and NSData extension methods from Common (why?) -> redefine them here in a way Swift sees them. + */ +class ByteArrayExtensions { + + companion object { + + fun toNSData(array: ByteArray): NSData { + return NSMutableData().apply { + if (array.isEmpty()) return@apply + array.usePinned { + appendBytes(it.addressOf(0), array.size.convert()) + } + } + } + + fun fromNSData(data: NSData): ByteArray { + val bytes: CPointer = data.bytes!!.reinterpret() + + return ByteArray(data.length.toInt()) { index -> bytes[index] } + } + + } +} \ No newline at end of file diff --git a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents index 8478b41f..ddf7d94c 100644 --- a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents +++ b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents @@ -77,7 +77,7 @@ - + diff --git a/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift b/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift index 3229d3bf..aff0cff9 100644 --- a/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift +++ b/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift @@ -16,7 +16,7 @@ let previewFlickerCodeTanChallenge = FlickerCodeTanChallenge(flickerCode: Flicke func createPreviewBanks() -> [IBankData] { - let bank1 = BankData(bankCode: "", userName: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: []) + let bank1 = BankData(bankCode: "", userName: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconData: nil, accounts: []) bank1.accounts = [ BankAccount(bank: bank1, productName: "Girokonto", identifier: "1234567890"), @@ -25,7 +25,7 @@ func createPreviewBanks() -> [IBankData] { ] - let bank2 = BankData(bankCode: "", userName: "", password: "", finTsServerAddress: "", bankName: "Kundenverarschebank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: []) + let bank2 = BankData(bankCode: "", userName: "", password: "", finTsServerAddress: "", bankName: "Kundenverarschebank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconData: nil, accounts: []) bank2.accounts = [ BankAccount(bank: bank2, productName: "Girokonto", identifier: "1234567890") diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift index a0d3d044..23b823e9 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift @@ -147,6 +147,19 @@ class CoreDataBankingPersistence: IBankingPersistence, ITransactionPartySearcher } + func saveBankIcon(bank: IBankData, iconUrl: String, fileExtension: String?) { + do { + if let remoteUrl = URL.encoded(iconUrl) { + let iconData = try Data(contentsOf: remoteUrl) + bank.iconData = mapper.map(iconData) + + saveOrUpdateBank(bank: bank, allBanks: []) + } + } catch { + NSLog("Could not get icon for bank \(bank) from url \(iconUrl): \(error)") + } + } + func saveUrlToFile(url: String, file: URL) { if let remoteUrl = URL.encoded(url) { if let fileData = try? Data(contentsOf: remoteUrl) { diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift index 5b82b64e..878d0a7e 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift @@ -6,8 +6,8 @@ import BankingUiSwift class Mapper { func map(_ bank: PersistedBankData) -> IBankData { - let mapped = BankData(bankCode: map(bank.bankCode), userName: map(bank.userName), password: map(bank.password), finTsServerAddress: map(bank.finTsServerAddress), bankName: map(bank.bankName), bic: map(bank.bic), customerName: map(bank.customerName), userId: map(bank.userId), iconUrl: bank.iconUrl, accounts: []) - + let mapped = BankData(bankCode: map(bank.bankCode), userName: map(bank.userName), password: map(bank.password), finTsServerAddress: map(bank.finTsServerAddress), bankName: map(bank.bankName), bic: map(bank.bic), customerName: map(bank.customerName), userId: map(bank.userId), iconData: map(bank.iconData), accounts: []) + mapped.wrongCredentialsEntered = bank.wrongCredentialsEntered mapped.savePassword = bank.savePassword mapped.userSetDisplayName = bank.userSetDisplayName @@ -36,8 +36,8 @@ class Mapper { mapped.bic = bank.bic mapped.customerName = bank.customerName mapped.userId = bank.userId - mapped.iconUrl = bank.iconUrl - + mapped.iconData = map(bank.iconData) + mapped.wrongCredentialsEntered = bank.wrongCredentialsEntered mapped.savePassword = bank.savePassword mapped.userSetDisplayName = bank.userSetDisplayName @@ -448,4 +448,20 @@ class Mapper { return string ?? "" } + func map(_ array: KotlinByteArray?) -> Data? { + if let array = array { + return ByteArrayExtensions.Companion().toNSData(array: array) + } + + return nil + } + + func map(_ data: Data?) -> KotlinByteArray? { + if let data = data { + return ByteArrayExtensions.Companion().fromNSData(data: data) + } + + return nil + } + } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconView.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconView.swift index 1651901b..6c762719 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconView.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconView.swift @@ -1,9 +1,10 @@ import SwiftUI +import BankingUiSwift struct IconView: View { - let iconUrl: String? + let iconData: KotlinByteArray? let defaultIconName: String @@ -12,22 +13,22 @@ struct IconView: View { var body: some View { - getBankIcon(self.iconUrl) + getBankIcon(self.iconData) .renderingMode(Image.TemplateRenderingMode.original) .resizable() .scaledToFit() .frame(width: Styles.AccountsIconWidth) } - private func getBankIcon(_ iconUrl: String?) -> Image { - if let iconUrl = iconUrl { - if let iconData = persistence.readContentOfFile(iconUrl) { - if let uiImage = UIImage(data: iconData) { - return Image(uiImage: uiImage) - } + private func getBankIcon(_ iconData: KotlinByteArray?) -> Image { + if let iconData = iconData { + let nsData = ByteArrayExtensions.Companion().toNSData(array: iconData) + + if let uiImage = UIImage(data: nsData) { + return Image(uiImage: uiImage) } } - + return Image(defaultIconName) } @@ -37,7 +38,7 @@ struct IconView: View { struct IconView_Previews: PreviewProvider { static var previews: some View { - IconView(iconUrl: nil, defaultIconName: "") + IconView(iconData: nil, defaultIconName: "") } } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift index 0fc87c3c..43fbb853 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift @@ -6,7 +6,7 @@ struct IconedTitleView: View { private var title: String - private var iconUrl: String? + private var iconData: KotlinByteArray? private var defaultIconName: String @@ -14,16 +14,16 @@ struct IconedTitleView: View { init(_ bank: IBankData, titleFont: Font? = nil) { - self.init(accountTitle: bank.displayName, iconUrl: bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) + self.init(accountTitle: bank.displayName, iconData: bank.iconData, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) } init(_ account: IBankAccount, titleFont: Font? = nil) { - self.init(accountTitle: account.displayName, iconUrl: account.bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) + self.init(accountTitle: account.displayName, iconData: account.bank.iconData, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) } - init(accountTitle: String, iconUrl: String?, defaultIconName: String, titleFont: Font? = nil) { + init(accountTitle: String, iconData: KotlinByteArray?, defaultIconName: String, titleFont: Font? = nil) { self.title = accountTitle - self.iconUrl = iconUrl + self.iconData = iconData self.defaultIconName = defaultIconName self.titleFont = titleFont @@ -32,7 +32,7 @@ struct IconedTitleView: View { var body: some View { HStack { - IconView(iconUrl: self.iconUrl, defaultIconName: self.defaultIconName) + IconView(iconData: self.iconData, defaultIconName: self.defaultIconName) Spacer() .frame(width: Styles.DefaultSpaceBetweenIconAndText) @@ -58,7 +58,7 @@ struct IconedTitleView: View { struct IconedAccountTitle_Previews: PreviewProvider { static var previews: some View { - IconedTitleView(accountTitle: "Abzockbank", iconUrl: nil, defaultIconName: Styles.AccountFallbackIcon) + IconedTitleView(accountTitle: "Abzockbank", iconData: nil, defaultIconName: Styles.AccountFallbackIcon) } } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift index ebca2171..eba70bd9 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift @@ -40,7 +40,7 @@ struct AccountTransactionListItem: View { if areMoreThanOneBanksTransactionsDisplayed { Spacer() - IconView(iconUrl: transaction.account.bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon) + IconView(iconData: transaction.account.bank.iconData, defaultIconName: Styles.AccountFallbackIcon) } Spacer() diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift index 6a126d8d..181ad18b 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift @@ -16,7 +16,7 @@ struct AllBanksListItem: View { Section { NavigationLink(destination: EmptyView(), isActive: .constant(false)) { // NavigationLink navigated to AccountTransactionsDialog twice. So i disabled NavigationLink and implemented manual navigation HStack { - IconedTitleView(accountTitle: "All accounts".localize(), iconUrl: nil, defaultIconName: Styles.AccountFallbackIcon, titleFont: .headline) + IconedTitleView(accountTitle: "All accounts".localize(), iconData: nil, defaultIconName: Styles.AccountFallbackIcon, titleFont: .headline) Spacer()