Implemented saving Image (and FlickerCode) settings

This commit is contained in:
dankito 2024-10-17 23:01:11 +02:00
parent 166219d7e3
commit a5b4540443
13 changed files with 132 additions and 13 deletions

View File

@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.persistence.entities.UiSettingsEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.model.settings.ImageSettings
interface BankingRepository {
@ -25,6 +26,11 @@ interface BankingRepository {
suspend fun saveUiSettings(settings: UiSettingsEntity)
fun getImageSettings(id: String): ImageSettings?
suspend fun saveImageSettings(settings: ImageSettings)
fun getAllBanks(): List<BankAccessEntity>
suspend fun persistBank(bank: BankAccess): BankAccessEntity

View File

@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity
import net.codinux.banking.persistence.entities.UiSettingsEntity
import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.model.settings.ImageSettings
import net.codinux.banking.ui.model.settings.TransactionsGrouping
class InMemoryBankingRepository(
@ -29,6 +30,8 @@ class InMemoryBankingRepository(
private var uiSettings: UiSettingsEntity = UiSettingsEntity(true, TransactionsGrouping.Month, true, true, true)
private var imageSettings = mutableMapOf<String, ImageSettings>()
override fun getAppSettings(): AppSettings? = appSettings
@ -43,6 +46,13 @@ class InMemoryBankingRepository(
}
override fun getImageSettings(id: String) = imageSettings[id]
override suspend fun saveImageSettings(settings: ImageSettings) {
imageSettings[settings.id] = settings
}
override fun getAllBanks(): List<BankAccessEntity> = banks.toList()
override suspend fun persistBank(bank: BankAccess): BankAccessEntity {

View File

@ -23,9 +23,9 @@ import net.codinux.banking.client.model.tan.TanMethod
import net.codinux.banking.client.model.tan.TanMethodType
import net.codinux.banking.persistence.entities.*
import net.codinux.banking.ui.model.AccountTransactionViewModel
import net.codinux.banking.ui.model.settings.AppAuthenticationMethod
import net.codinux.banking.ui.model.settings.*
import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.model.settings.TransactionsGrouping
import net.codinux.banking.ui.model.settings.ImageSettings
import net.codinux.log.logger
import kotlin.enums.EnumEntries
import kotlin.js.JsName
@ -39,7 +39,7 @@ open class SqliteBankingRepository : BankingRepository {
private val schema = BankmeisterDb.Schema
private val sqlDriver = createSqlDriverDriver("Bankmeister.db", schema, 1L)
private val sqlDriver = createSqlDriverDriver("Bankmeister.db", schema, 2L)
private val database = BankmeisterDb(sqlDriver)
@ -89,6 +89,16 @@ open class SqliteBankingRepository : BankingRepository {
}
override fun getImageSettings(id: String): ImageSettings? =
settingsQueries.getImageSettings(id) { height, frequency ->
ImageSettings(id, mapToInt(height), mapToInt(frequency))
}.executeAsOneOrNull()
override suspend fun saveImageSettings(settings: ImageSettings) {
settingsQueries.upsertImageSettings(settings.id, mapInt(settings.height), mapInt(settings.frequency))
}
override fun getAllBanks(): List<BankAccessEntity> {
val bankAccounts = getAllBankAccounts().groupBy { it.bankId }
val tanMethods = getAllTanMethods().groupBy { it.bankId }.mapValues { it.value.toMutableList() }

View File

@ -0,0 +1,11 @@
package net.codinux.banking.ui.model.settings
class ImageSettings(
val id: String,
var height: Int,
var frequency: Int? = null // only needed for flicker code
) {
override fun toString() = "$id $height${if (frequency != null) " (frequency = $frequency)" else ""}"
}

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS ImageSettings (
id TEXT PRIMARY KEY,
height INTEGER NOT NULL,
width INTEGER, -- not used right now, add it just in case
frequency INTEGER
);

View File

@ -70,3 +70,22 @@ SELECT * FROM UiSettings WHERE id = 1;
upsertUiSettings:
INSERT OR REPLACE INTO UiSettings(id, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors)
VALUES (1, ?, ?, ?, ?, ?);
CREATE TABLE IF NOT EXISTS ImageSettings (
id TEXT PRIMARY KEY,
height INTEGER NOT NULL,
width INTEGER, -- not used right now, add it just in case
frequency INTEGER
);
getImageSettings:
SELECT height, frequency FROM ImageSettings WHERE id = ?;
upsertImageSettings:
INSERT OR REPLACE INTO ImageSettings(id, height, frequency)
VALUES (?, ?, ?);

View File

@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import net.codinux.banking.client.model.tan.FlickerCode
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.model.Config.NewLine
import net.codinux.banking.ui.service.tan.Bit
import net.codinux.banking.ui.service.tan.FlickerCodeAnimator
@ -22,23 +23,38 @@ import net.codinux.banking.ui.service.tan.Step
private const val FrequencyStepSize = 2
private val DefaultStripesHeight = 240.dp
private val StripesHeightStepSize = 7.dp
private val StripesWidthStepSize = 2.dp
private val SpaceBetweenStripesStepSize = 1.dp
private val bankingService = DI.bankingService
@Composable
fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Black) {
val animator = remember { FlickerCodeAnimator() }
var stripesHeight by remember { mutableStateOf(240.dp) }
val flickerCodeSettings by remember { mutableStateOf(bankingService.getImageSettingsOrCreateDefault("FlickerCode", 240)) }
var stripesWidth by remember { mutableStateOf(45.dp) }
val sizeFactor by remember { mutableStateOf(
if (flickerCodeSettings.height == 240) {
1
} else {
val diff = flickerCodeSettings.height.dp - DefaultStripesHeight
var spaceBetweenStripes by remember { mutableStateOf(15.dp) }
(diff / StripesHeightStepSize).toInt()
}
) }
var frequency by remember { mutableStateOf(FlickerCodeAnimator.DefaultFrequency) }
var stripesHeight by remember { mutableStateOf(DefaultStripesHeight + StripesHeightStepSize.times(sizeFactor)) }
var stripesWidth by remember { mutableStateOf(45.dp + StripesWidthStepSize.times(sizeFactor)) }
var spaceBetweenStripes by remember { mutableStateOf(15.dp + SpaceBetweenStripesStepSize.times(sizeFactor)) }
var frequency by remember { mutableStateOf(flickerCodeSettings.frequency ?: FlickerCodeAnimator.DefaultFrequency) }
var isPaused by remember { mutableStateOf(false) }
@ -51,6 +67,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl
stripesWidth = width
stripesHeight = height
spaceBetweenStripes = spaceBetween
flickerCodeSettings.height = height.value.toInt()
bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope)
}
fun decreaseSize() {
@ -76,6 +95,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl
frequency = newFrequency
animator.setFrequency(newFrequency)
flickerCodeSettings.frequency = newFrequency
bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope)
}
fun decreaseFrequency() {

View File

@ -9,11 +9,16 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.service.createImageBitmap
private val bankingService = DI.bankingService
@Composable
fun ImageView(
imageBytes: ByteArray,
imageSettingsId: String,
contentDescription: String,
initialImageHeight: Int = 300,
minImageHeight: Int = 0,
@ -22,11 +27,19 @@ fun ImageView(
textColor: Color = Colors.MaterialThemeTextColor,
) {
var imageHeight by remember { mutableStateOf(initialImageHeight) }
val imageSettings = bankingService.getImageSettingsOrCreateDefault(imageSettingsId, initialImageHeight)
var imageHeight by remember { mutableStateOf(imageSettings.height) }
val coroutineScope = rememberCoroutineScope()
fun changeImageSize(by: Int) {
imageHeight += by
imageSettings.height = imageHeight
bankingService.saveImageSettingsDebounced(imageSettings, coroutineScope)
}

View File

@ -36,7 +36,7 @@ object DI {
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
val bankingService by lazy { BankingService(uiState, uiSettings, bankingRepository, bankFinder) }
val bankingService by lazy { BankingService(uiState, uiSettings, uiService, bankingRepository, bankFinder) }
fun setRepository(repository: BankingRepository) {

View File

@ -147,7 +147,8 @@ fun EnterTanDialog(tanChallengeReceived: TanChallengeReceived, onDismiss: () ->
tanImage.imageBytesBase64?.let { imageBytesBase64 ->
val imageBytes = Base64.decode(imageBytesBase64)
ImageView(imageBytes, "Bild mit enkodierter TAN", 250, 100, 500, textColor = textColor)
// if it becomes necessary may also add the bank to ImageSettings.id to make ImageSettings bank specific
ImageView(imageBytes, challenge.selectedTanMethod.type.toString(), "Bild mit enkodierter TAN", 250, 100, 500, textColor = textColor)
}
}
}

View File

@ -86,7 +86,7 @@ fun CreateEpcQrCodeScreen(onClosed: () -> Unit) {
Text("Mit EPC QR Codes, welche als GiroCode, scan2code, ... vermarktet werden, können Überweisungsdaten ganz einfach von Banking Apps eingelesen werden.")
Text("Hier können Sie Ihren eigenen erstellen, so dass jemand Ihre Überweisungsdaten einlesen und Ihnen ganz schnell Geld überweisen kann.")
} else {
ImageView(epcQrCodeBytes!!, "Erzeugter EPC QR Code", 350, 100, 700)
ImageView(epcQrCodeBytes!!, "EpcQrCode", "Erzeugter EPC QR Code", 350, 100, 700)
Row(Modifier.fillMaxWidth().padding(top = 8.dp), horizontalArrangement = Arrangement.Center) {
Text("Scannen Sie diesen Code auf einem anderen Gerät mit einer Banking App, z. B. Bankmeister", textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp))

View File

@ -28,6 +28,7 @@ import net.codinux.banking.ui.model.BankInfo
import net.codinux.banking.ui.model.error.*
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
import net.codinux.banking.ui.model.settings.ImageSettings
import net.codinux.banking.ui.model.settings.AppSettings
import net.codinux.banking.ui.settings.UiSettings
import net.codinux.banking.ui.state.UiState
@ -39,6 +40,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
class BankingService(
private val uiState: UiState,
private val uiSettings: UiSettings,
private val uiService: UiService,
private val bankingRepository: BankingRepository,
private val bankFinder: BankFinder
) {
@ -93,6 +95,22 @@ class BankingService(
))
fun getImageSettingsOrCreateDefault(id: String, defaultHeight: Int): ImageSettings =
bankingRepository.getImageSettings(id) ?: ImageSettings(id, defaultHeight)
fun saveImageSettingsDebounced(settings: ImageSettings, coroutineScope: CoroutineScope) {
uiService.debounce(coroutineScope, 1_000) {
saveImageSettings(settings)
}
}
suspend fun saveImageSettings(settings: ImageSettings) = try {
bankingRepository.saveImageSettings(settings)
} catch (e: Throwable) {
log.error(e) { "Could not save ImageSettings ${settings.id}"}
}
fun getAllBanks() = bankingRepository.getAllBanks()
suspend fun updateBank(bank: BankAccessEntity, loginName: String, password: String, bankName: String?) {

View File

@ -4,9 +4,9 @@ import kotlinx.coroutines.*
class UiService {
fun debounce(coroutineScope: CoroutineScope, action: () -> Unit): Job {
fun debounce(coroutineScope: CoroutineScope, delayMillis: Long = 250, action: suspend () -> Unit): Job {
return coroutineScope.launch {
delay(250)
delay(delayMillis)
action()
}
}