Showing bank's favicon (if available)
This commit is contained in:
parent
4856ced158
commit
d9a3e942e9
|
@ -1,6 +1,11 @@
|
||||||
package net.codinux.banking.ui
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
|
get() = Dispatchers.IO
|
||||||
|
|
||||||
class AndroidPlatform : Platform {
|
class AndroidPlatform : Platform {
|
||||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
||||||
|
|
|
@ -3,6 +3,10 @@ package net.codinux.banking.ui.service
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
||||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size).asImageBitmap()
|
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size).asImageBitmap()
|
||||||
|
|
||||||
|
actual fun fetchBytesFromUrl(url: String): ByteArray =
|
||||||
|
URL(url).openStream().buffered().use { it.readBytes() }
|
|
@ -1,5 +1,10 @@
|
||||||
package net.codinux.banking.ui
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
|
||||||
|
expect val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
|
|
||||||
expect fun getPlatform(): Platform
|
expect fun getPlatform(): Platform
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package net.codinux.banking.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import net.codinux.banking.client.model.UserAccount
|
||||||
|
import net.codinux.banking.client.model.UserAccountViewInfo
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
import net.codinux.banking.ui.model.BankInfo
|
||||||
|
import net.dankito.banking.banklistcreator.prettifier.BankingGroupMapper
|
||||||
|
|
||||||
|
private val bankIconService = DI.bankIconService
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BankIcon(userAccount: UserAccount?, modifier: Modifier = Modifier) {
|
||||||
|
val iconUrl by remember(userAccount?.bic) { mutableStateOf(userAccount?.let { bankIconService.findIconForBank(it) }) }
|
||||||
|
|
||||||
|
BankIcon(iconUrl, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val bankingGroupMapper = BankingGroupMapper()
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BankIcon(bank: BankInfo, modifier: Modifier = Modifier) {
|
||||||
|
val iconUrl by remember(bank.bic) { mutableStateOf(bankIconService.findIconForBank(bank.name, bankingGroupMapper.getBankingGroup(bank.name, bank.bic))) }
|
||||||
|
|
||||||
|
BankIcon(iconUrl, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BankIcon(userAccount: UserAccountViewInfo?, modifier: Modifier = Modifier) {
|
||||||
|
val iconUrl = userAccount?.let { bankIconService.findIconForBank(it.bankName, it.bankingGroup) }
|
||||||
|
|
||||||
|
BankIcon(iconUrl, modifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BankIcon(iconUrl: String?, modifier: Modifier = Modifier) {
|
||||||
|
Column(modifier) {
|
||||||
|
iconUrl?.let {
|
||||||
|
IconForUrl(iconUrl, "Favicon of this bank", Modifier.width(16.dp).height(16.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package net.codinux.banking.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import net.codinux.banking.ui.IOorDefault
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
|
private val imageCache = DI.imageCache
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun IconForUrl(iconUrl: String, contentDescription: String, modifier: Modifier = Modifier) {
|
||||||
|
var imageBitmap by remember(iconUrl) { mutableStateOf<ImageBitmap?>(null) }
|
||||||
|
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
if (iconUrl.endsWith(".svg") == false) { // SVG is not supported on Android
|
||||||
|
coroutineScope.launch(Dispatchers.IOorDefault) {
|
||||||
|
val received = imageCache.getImageBitmap(iconUrl)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
imageBitmap = received
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
imageBitmap?.let { imageBitmap ->
|
||||||
|
Image(imageBitmap, contentDescription, modifier)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,25 +9,30 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import net.codinux.banking.client.model.UserAccount
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
|
||||||
private val formatUtil = DI.formatUtil
|
private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionListItem(transaction: AccountTransactionViewModel, backgroundColor: Color) {
|
fun TransactionListItem(userAccount: UserAccount?, transaction: AccountTransactionViewModel, backgroundColor: Color) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
.background(color = backgroundColor)
|
.background(color = backgroundColor)
|
||||||
.padding(horizontal = 6.dp, vertical = 6.dp)
|
.padding(horizontal = 6.dp, vertical = 6.dp)
|
||||||
) {
|
) {
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
|
Row {
|
||||||
|
BankIcon(userAccount, Modifier.padding(end = 6.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = transaction.otherPartyName ?: "",
|
text = transaction.otherPartyName ?: "",
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.Amount
|
||||||
import net.codinux.banking.ui.extensions.toBigDecimal
|
import net.codinux.banking.ui.extensions.toBigDecimal
|
||||||
import net.codinux.banking.ui.forms.RoundedCornersCard
|
import net.codinux.banking.ui.forms.RoundedCornersCard
|
||||||
|
@ -28,6 +27,11 @@ private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionsList(uiState: UiState) {
|
fun TransactionsList(uiState: UiState) {
|
||||||
|
val users by uiState.userAccounts.collectAsState()
|
||||||
|
val usersById by remember(users) {
|
||||||
|
derivedStateOf { users.associateBy { it.id } }
|
||||||
|
}
|
||||||
|
|
||||||
val transactions by uiState.transactions.collectAsState()
|
val transactions by uiState.transactions.collectAsState()
|
||||||
|
|
||||||
val groupedByMonth by remember(transactions) {
|
val groupedByMonth by remember(transactions) {
|
||||||
|
@ -53,7 +57,8 @@ fun TransactionsList(uiState: UiState) {
|
||||||
RoundedCornersCard {
|
RoundedCornersCard {
|
||||||
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
|
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
|
||||||
monthTransactions.forEachIndexed { index, transaction ->
|
monthTransactions.forEachIndexed { index, transaction ->
|
||||||
TransactionListItem(transaction, if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent)
|
val backgroundColor = if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent
|
||||||
|
TransactionListItem(usersById[transaction.userAccountId], transaction, backgroundColor)
|
||||||
|
|
||||||
if (index < monthTransactions.size - 1) {
|
if (index < monthTransactions.size - 1) {
|
||||||
Divider(color = Colors.Zinc200, thickness = 1.dp)
|
Divider(color = Colors.Zinc200, thickness = 1.dp)
|
||||||
|
|
|
@ -18,8 +18,11 @@ object DI {
|
||||||
|
|
||||||
val formatUtil = FormatUtil()
|
val formatUtil = FormatUtil()
|
||||||
|
|
||||||
|
val imageCache = ImageCache()
|
||||||
|
|
||||||
val bankFinder = BankFinder()
|
val bankFinder = BankFinder()
|
||||||
|
|
||||||
|
val bankIconService = BankIconService()
|
||||||
|
|
||||||
|
|
||||||
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
package net.codinux.banking.ui.dialogs
|
package net.codinux.banking.ui.dialogs
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Close
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.codinux.banking.ui.forms.*
|
import net.codinux.banking.ui.composables.BankIcon
|
||||||
import net.codinux.banking.ui.model.BankInfo
|
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.config.Style
|
import net.codinux.banking.ui.forms.*
|
||||||
|
import net.codinux.banking.ui.model.BankInfo
|
||||||
|
|
||||||
|
|
||||||
private val bankingService = DI.bankingService
|
private val bankingService = DI.bankingService
|
||||||
|
@ -72,7 +68,16 @@ fun AddAccountDialog(
|
||||||
getItemTitle = { bank -> bank.name },
|
getItemTitle = { bank -> bank.name },
|
||||||
fetchSuggestions = { query -> bankingService.findBanks(query) }
|
fetchSuggestions = { query -> bankingService.findBanks(query) }
|
||||||
) { bank ->
|
) { bank ->
|
||||||
Text(bank.name)
|
Row {
|
||||||
|
BankIcon(bank, Modifier.padding(end = 6.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
bank.name,
|
||||||
|
Modifier.fillMaxWidth(),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Row(Modifier.fillMaxWidth().padding(top = 8.dp)) {
|
Row(Modifier.fillMaxWidth().padding(top = 8.dp)) {
|
||||||
Text(bank.bankCode)
|
Text(bank.bankCode)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import bankmeister.composeapp.generated.resources.zoom_in
|
||||||
import bankmeister.composeapp.generated.resources.zoom_out
|
import bankmeister.composeapp.generated.resources.zoom_out
|
||||||
import net.codinux.banking.client.model.tan.AllowedTanFormat
|
import net.codinux.banking.client.model.tan.AllowedTanFormat
|
||||||
import net.codinux.banking.client.model.tan.EnterTanResult
|
import net.codinux.banking.client.model.tan.EnterTanResult
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.composables.BankIcon
|
||||||
import net.codinux.banking.ui.config.Internationalization
|
import net.codinux.banking.ui.config.Internationalization
|
||||||
import net.codinux.banking.ui.forms.OutlinedTextField
|
import net.codinux.banking.ui.forms.OutlinedTextField
|
||||||
import net.codinux.banking.ui.model.TanChallengeReceived
|
import net.codinux.banking.ui.model.TanChallengeReceived
|
||||||
|
@ -63,6 +63,8 @@ fun EnterTanDialog(tanChallengeReceived: TanChallengeReceived, onDismiss: () ->
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Row {
|
Row {
|
||||||
|
BankIcon(challenge.user, Modifier.padding(end = 6.dp))
|
||||||
|
|
||||||
Text("${challenge.user.bankName}, Nutzer ${challenge.user.loginName}${challenge.account?.let { ", Konto ${it.productName ?: it.identifier}" } ?: ""}")
|
Text("${challenge.user.bankName}, Nutzer ${challenge.user.loginName}${challenge.account?.let { ", Konto ${it.productName ?: it.identifier}" } ?: ""}")
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package net.codinux.banking.ui.service
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.BankingGroup
|
||||||
|
import net.codinux.banking.client.model.UserAccount
|
||||||
|
|
||||||
|
class BankIconService { // TODO: extract to a common library
|
||||||
|
|
||||||
|
fun findIconForBank(user: UserAccount) = findIconForBank(user.bankName, user.bankingGroup)
|
||||||
|
|
||||||
|
fun findIconForBank(bankName: String, bankingGroup: BankingGroup? = null): String? = when (bankingGroup) {
|
||||||
|
BankingGroup.Sparkasse -> "https://sparkasse.de/favicon-32x32.png"
|
||||||
|
BankingGroup.DKB -> "https://www.ib.dkb.de/favicon.ico"
|
||||||
|
BankingGroup.OldenburgischeLandesbank -> "https://olb.de/assets/img/icon/olb/favicon-32x32.png"
|
||||||
|
|
||||||
|
BankingGroup.VolksUndRaiffeisenbanken -> "https://vr.de/favicon.ico"
|
||||||
|
BankingGroup.Sparda -> "https://www.sparda.de/hidden/layout/images/touchicons/favicon-32x32.png"
|
||||||
|
BankingGroup.PSD -> "https://www.psd-muenchen.de/favicon.ico"
|
||||||
|
BankingGroup.GLS -> "https://gls.de/assets/dist/img/icons/v2/favicon-32x32.png" // "https://gls.de/favicon.ico"
|
||||||
|
|
||||||
|
BankingGroup.DeutscheBank -> "https://www.deutsche-bank.de/etc/designs/db-eccs-pws-pwcc/assets/db-favicon-167x167.png" // "https://www.deutsche-bank.de/etc/designs/db-eccs-pws-pwcc/assets/favicon.svg" // https://master.dwebcms.db.com/application/themes/default/favicon/favicon-32x32.png
|
||||||
|
BankingGroup.Postbank -> "https://postbank.de/etc/designs/pb-eccs-pb/icons/pb-favicon-167x167.png" // "https://postbank.de/etc/designs/pb-eccs-pb/icons/favicon.svg"
|
||||||
|
|
||||||
|
BankingGroup.Commerzbank -> "https://commerzbank.de/ms/media/favicons/apple-touch-icon-57x57_A.png"
|
||||||
|
BankingGroup.Comdirect -> "https://comdirect.de/favicon.ico"
|
||||||
|
|
||||||
|
BankingGroup.Unicredit -> "https://hypovereinsbank.de/etc/designs/hypovereinsbank/img/favicon/favicon.ico"
|
||||||
|
BankingGroup.Targobank -> "https://targobank.de/de/images/favicon/favicon.png" // https://targobank.de/favicon.ico
|
||||||
|
BankingGroup.ING -> "https://ing.de/favicon.ico" // "https://cdn.ing.de/ing-cms-ui/129.0.2/assets/favicons/apple-touch-icon.png"
|
||||||
|
BankingGroup.Santander -> "https://www.santander.de/ressourcen/img/favicons/favicon.ico"
|
||||||
|
|
||||||
|
BankingGroup.Norisbank -> "https://norisbank.de/etc/designs/db-eccs-nb/assets/nb-favicon-167x167.png" // ""https://norisbank.de/etc/designs/db-eccs-nb/assets/favicon.svg"
|
||||||
|
BankingGroup.Degussa -> "https://www.degussa-bank.de/o/degussa-bank-theme/images/favicon.ico"
|
||||||
|
|
||||||
|
BankingGroup.N26 -> "https://n26.de/favicon.ico"
|
||||||
|
|
||||||
|
else -> null // TODO: call Favicon web service
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package net.codinux.banking.ui.service
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import net.codinux.log.logger
|
||||||
|
|
||||||
|
// TODO: save retrieved bytes to disk so that they don't have to be retrieved on the next app start again
|
||||||
|
class ImageCache {
|
||||||
|
|
||||||
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
private val imageBytesCache = mutableMapOf<String, Deferred<ByteArray?>>()
|
||||||
|
|
||||||
|
private val imageBitmapCache = mutableMapOf<String, Deferred<ImageBitmap?>>()
|
||||||
|
|
||||||
|
private val log by logger()
|
||||||
|
|
||||||
|
// would be create but didn't get it to work with Ktor. Had therefor to introduce platform specific URL loading with loadImageBytes()
|
||||||
|
// simply re-using KtorWebClient as it already has all platform specific engines set up
|
||||||
|
// private val webClient = object : KtorWebClient() {
|
||||||
|
//
|
||||||
|
// val ktorClient = client
|
||||||
|
// init {
|
||||||
|
// client
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
suspend fun getImageBytes(url: String): ByteArray? = coroutineScope {
|
||||||
|
mutex.withLock {
|
||||||
|
var cached = imageBytesCache[url]
|
||||||
|
if (cached == null || cached.isCancelled) {
|
||||||
|
//LAZY - to free the mutex lock as fast as possible
|
||||||
|
val loadJob = async(start = CoroutineStart.LAZY) {
|
||||||
|
loadImage(url) // TODO: in case of failure, remove url from cache and try again?
|
||||||
|
}
|
||||||
|
|
||||||
|
imageBytesCache[url] = loadJob
|
||||||
|
loadJob
|
||||||
|
} else {
|
||||||
|
cached
|
||||||
|
}
|
||||||
|
}.await()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getImageBitmap(url: String): ImageBitmap? = coroutineScope {
|
||||||
|
getImageBytes(url)?.let {
|
||||||
|
createImageBitmap(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun loadImage(url: String): ByteArray? {
|
||||||
|
return try {
|
||||||
|
// val clientResponse = webClient.ktorClient.get(url)
|
||||||
|
// if (clientResponse.status.isSuccess()) {
|
||||||
|
// clientResponse.readBytes()
|
||||||
|
// } else {
|
||||||
|
// null
|
||||||
|
// }
|
||||||
|
|
||||||
|
fetchBytesFromUrl(url)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Failed to load image from url '$url'" }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,3 +3,5 @@ package net.codinux.banking.ui.service
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
|
||||||
expect fun createImageBitmap(imageBytes: ByteArray): ImageBitmap
|
expect fun createImageBitmap(imageBytes: ByteArray): ImageBitmap
|
||||||
|
|
||||||
|
expect fun fetchBytesFromUrl(url: String): ByteArray
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
|
get() = Dispatchers.IO
|
||||||
|
|
||||||
|
|
||||||
|
class JVMPlatform: Platform {
|
||||||
|
override val name: String = "Java ${System.getProperty("java.version")}"
|
||||||
|
|
||||||
|
override val type: PlatformType = PlatformType.JVM
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun getPlatform(): Platform = JVMPlatform()
|
|
@ -1,9 +0,0 @@
|
||||||
package net.codinux.banking.ui
|
|
||||||
|
|
||||||
class JVMPlatform: Platform {
|
|
||||||
override val name: String = "Java ${System.getProperty("java.version")}"
|
|
||||||
|
|
||||||
override val type: PlatformType = PlatformType.JVM
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun getPlatform(): Platform = JVMPlatform()
|
|
|
@ -2,6 +2,7 @@ package net.codinux.banking.ui.dialogs
|
||||||
|
|
||||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import net.codinux.banking.client.model.BankingGroup
|
||||||
import net.codinux.banking.client.model.UserAccountViewInfo
|
import net.codinux.banking.client.model.UserAccountViewInfo
|
||||||
import net.codinux.banking.client.model.tan.*
|
import net.codinux.banking.client.model.tan.*
|
||||||
import net.codinux.banking.ui.model.TanChallengeReceived
|
import net.codinux.banking.ui.model.TanChallengeReceived
|
||||||
|
@ -10,7 +11,7 @@ import net.codinux.banking.ui.model.TanChallengeReceived
|
||||||
@Composable
|
@Composable
|
||||||
fun EnterTanDialogPreview_EnterTan() {
|
fun EnterTanDialogPreview_EnterTan() {
|
||||||
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
||||||
val user = UserAccountViewInfo("12345678", "SupiDupiNutzer", "Abzockbank")
|
val user = UserAccountViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, user = user)
|
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, user = user)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
|
@ -24,7 +25,7 @@ fun EnterTanDialogPreview_TanImage() {
|
||||||
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
||||||
val tanImage = TanImage("image/png", tanImageBytes)
|
val tanImage = TanImage("image/png", tanImageBytes)
|
||||||
|
|
||||||
val user = UserAccountViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank")
|
val user = UserAccountViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank", BankingGroup.Comdirect)
|
||||||
|
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, user)
|
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, user)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@ package net.codinux.banking.ui.service
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||||
import org.jetbrains.skia.Image
|
import org.jetbrains.skia.Image
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
||||||
Image.makeFromEncoded(imageBytes).toComposeImageBitmap()
|
Image.makeFromEncoded(imageBytes).toComposeImageBitmap()
|
||||||
|
|
||||||
|
actual fun fetchBytesFromUrl(url: String): ByteArray =
|
||||||
|
URL(url).openStream().buffered().use { it.readAllBytes() }
|
|
@ -1,5 +1,12 @@
|
||||||
package net.codinux.banking.ui
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
|
get() = Dispatchers.Default
|
||||||
|
|
||||||
|
|
||||||
class JsPlatform: Platform {
|
class JsPlatform: Platform {
|
||||||
override val name: String = "Web with Kotlin/Js"
|
override val name: String = "Web with Kotlin/Js"
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,5 @@ import org.jetbrains.skia.Image
|
||||||
|
|
||||||
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
actual fun createImageBitmap(imageBytes: ByteArray): ImageBitmap =
|
||||||
Image.makeFromEncoded(imageBytes).toComposeImageBitmap()
|
Image.makeFromEncoded(imageBytes).toComposeImageBitmap()
|
||||||
|
|
||||||
|
actual fun fetchBytesFromUrl(url: String): ByteArray = ByteArray(0)
|
Loading…
Reference in New Issue