Implemented saving Image (and FlickerCode) settings
This commit is contained in:
parent
166219d7e3
commit
a5b4540443
|
@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity
|
||||||
import net.codinux.banking.persistence.entities.UiSettingsEntity
|
import net.codinux.banking.persistence.entities.UiSettingsEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
import net.codinux.banking.ui.model.settings.AppSettings
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.model.settings.ImageSettings
|
||||||
|
|
||||||
interface BankingRepository {
|
interface BankingRepository {
|
||||||
|
|
||||||
|
@ -25,6 +26,11 @@ interface BankingRepository {
|
||||||
suspend fun saveUiSettings(settings: UiSettingsEntity)
|
suspend fun saveUiSettings(settings: UiSettingsEntity)
|
||||||
|
|
||||||
|
|
||||||
|
fun getImageSettings(id: String): ImageSettings?
|
||||||
|
|
||||||
|
suspend fun saveImageSettings(settings: ImageSettings)
|
||||||
|
|
||||||
|
|
||||||
fun getAllBanks(): List<BankAccessEntity>
|
fun getAllBanks(): List<BankAccessEntity>
|
||||||
|
|
||||||
suspend fun persistBank(bank: BankAccess): BankAccessEntity
|
suspend fun persistBank(bank: BankAccess): BankAccessEntity
|
||||||
|
|
|
@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity
|
||||||
import net.codinux.banking.persistence.entities.UiSettingsEntity
|
import net.codinux.banking.persistence.entities.UiSettingsEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
import net.codinux.banking.ui.model.settings.AppSettings
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.model.settings.ImageSettings
|
||||||
import net.codinux.banking.ui.model.settings.TransactionsGrouping
|
import net.codinux.banking.ui.model.settings.TransactionsGrouping
|
||||||
|
|
||||||
class InMemoryBankingRepository(
|
class InMemoryBankingRepository(
|
||||||
|
@ -29,6 +30,8 @@ class InMemoryBankingRepository(
|
||||||
|
|
||||||
private var uiSettings: UiSettingsEntity = UiSettingsEntity(true, TransactionsGrouping.Month, true, true, true)
|
private var uiSettings: UiSettingsEntity = UiSettingsEntity(true, TransactionsGrouping.Month, true, true, true)
|
||||||
|
|
||||||
|
private var imageSettings = mutableMapOf<String, ImageSettings>()
|
||||||
|
|
||||||
|
|
||||||
override fun getAppSettings(): AppSettings? = appSettings
|
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 fun getAllBanks(): List<BankAccessEntity> = banks.toList()
|
||||||
|
|
||||||
override suspend fun persistBank(bank: BankAccess): BankAccessEntity {
|
override suspend fun persistBank(bank: BankAccess): BankAccessEntity {
|
||||||
|
|
|
@ -23,9 +23,9 @@ import net.codinux.banking.client.model.tan.TanMethod
|
||||||
import net.codinux.banking.client.model.tan.TanMethodType
|
import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
import net.codinux.banking.persistence.entities.*
|
import net.codinux.banking.persistence.entities.*
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
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.AppSettings
|
||||||
import net.codinux.banking.ui.model.settings.TransactionsGrouping
|
import net.codinux.banking.ui.model.settings.ImageSettings
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import kotlin.enums.EnumEntries
|
import kotlin.enums.EnumEntries
|
||||||
import kotlin.js.JsName
|
import kotlin.js.JsName
|
||||||
|
@ -39,7 +39,7 @@ open class SqliteBankingRepository : BankingRepository {
|
||||||
|
|
||||||
private val schema = BankmeisterDb.Schema
|
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)
|
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> {
|
override fun getAllBanks(): List<BankAccessEntity> {
|
||||||
val bankAccounts = getAllBankAccounts().groupBy { it.bankId }
|
val bankAccounts = getAllBankAccounts().groupBy { it.bankId }
|
||||||
val tanMethods = getAllTanMethods().groupBy { it.bankId }.mapValues { it.value.toMutableList() }
|
val tanMethods = getAllTanMethods().groupBy { it.bankId }.mapValues { it.value.toMutableList() }
|
||||||
|
|
|
@ -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 ""}"
|
||||||
|
}
|
|
@ -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
|
||||||
|
);
|
|
@ -70,3 +70,22 @@ SELECT * FROM UiSettings WHERE id = 1;
|
||||||
upsertUiSettings:
|
upsertUiSettings:
|
||||||
INSERT OR REPLACE INTO UiSettings(id, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors)
|
INSERT OR REPLACE INTO UiSettings(id, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors)
|
||||||
VALUES (1, ?, ?, ?, ?, ?);
|
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 (?, ?, ?);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.client.model.tan.FlickerCode
|
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.model.Config.NewLine
|
||||||
import net.codinux.banking.ui.service.tan.Bit
|
import net.codinux.banking.ui.service.tan.Bit
|
||||||
import net.codinux.banking.ui.service.tan.FlickerCodeAnimator
|
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 const val FrequencyStepSize = 2
|
||||||
|
|
||||||
|
private val DefaultStripesHeight = 240.dp
|
||||||
private val StripesHeightStepSize = 7.dp
|
private val StripesHeightStepSize = 7.dp
|
||||||
private val StripesWidthStepSize = 2.dp
|
private val StripesWidthStepSize = 2.dp
|
||||||
private val SpaceBetweenStripesStepSize = 1.dp
|
private val SpaceBetweenStripesStepSize = 1.dp
|
||||||
|
|
||||||
|
private val bankingService = DI.bankingService
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Black) {
|
fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Black) {
|
||||||
|
|
||||||
val animator = remember { FlickerCodeAnimator() }
|
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) }
|
var isPaused by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
@ -51,6 +67,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl
|
||||||
stripesWidth = width
|
stripesWidth = width
|
||||||
stripesHeight = height
|
stripesHeight = height
|
||||||
spaceBetweenStripes = spaceBetween
|
spaceBetweenStripes = spaceBetween
|
||||||
|
|
||||||
|
flickerCodeSettings.height = height.value.toInt()
|
||||||
|
bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decreaseSize() {
|
fun decreaseSize() {
|
||||||
|
@ -76,6 +95,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl
|
||||||
frequency = newFrequency
|
frequency = newFrequency
|
||||||
|
|
||||||
animator.setFrequency(newFrequency)
|
animator.setFrequency(newFrequency)
|
||||||
|
|
||||||
|
flickerCodeSettings.frequency = newFrequency
|
||||||
|
bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decreaseFrequency() {
|
fun decreaseFrequency() {
|
||||||
|
|
|
@ -9,11 +9,16 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
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.service.createImageBitmap
|
import net.codinux.banking.ui.service.createImageBitmap
|
||||||
|
|
||||||
|
|
||||||
|
private val bankingService = DI.bankingService
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ImageView(
|
fun ImageView(
|
||||||
imageBytes: ByteArray,
|
imageBytes: ByteArray,
|
||||||
|
imageSettingsId: String,
|
||||||
contentDescription: String,
|
contentDescription: String,
|
||||||
initialImageHeight: Int = 300,
|
initialImageHeight: Int = 300,
|
||||||
minImageHeight: Int = 0,
|
minImageHeight: Int = 0,
|
||||||
|
@ -22,11 +27,19 @@ fun ImageView(
|
||||||
textColor: Color = Colors.MaterialThemeTextColor,
|
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) {
|
fun changeImageSize(by: Int) {
|
||||||
imageHeight += by
|
imageHeight += by
|
||||||
|
|
||||||
|
imageSettings.height = imageHeight
|
||||||
|
|
||||||
|
bankingService.saveImageSettingsDebounced(imageSettings, coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ object DI {
|
||||||
|
|
||||||
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
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) {
|
fun setRepository(repository: BankingRepository) {
|
||||||
|
|
|
@ -147,7 +147,8 @@ fun EnterTanDialog(tanChallengeReceived: TanChallengeReceived, onDismiss: () ->
|
||||||
tanImage.imageBytesBase64?.let { imageBytesBase64 ->
|
tanImage.imageBytesBase64?.let { imageBytesBase64 ->
|
||||||
val imageBytes = Base64.decode(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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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("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.")
|
Text("Hier können Sie Ihren eigenen erstellen, so dass jemand Ihre Überweisungsdaten einlesen und Ihnen ganz schnell Geld überweisen kann.")
|
||||||
} else {
|
} 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) {
|
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))
|
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))
|
||||||
|
|
|
@ -28,6 +28,7 @@ import net.codinux.banking.ui.model.BankInfo
|
||||||
import net.codinux.banking.ui.model.error.*
|
import net.codinux.banking.ui.model.error.*
|
||||||
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
||||||
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
|
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.model.settings.AppSettings
|
||||||
import net.codinux.banking.ui.settings.UiSettings
|
import net.codinux.banking.ui.settings.UiSettings
|
||||||
import net.codinux.banking.ui.state.UiState
|
import net.codinux.banking.ui.state.UiState
|
||||||
|
@ -39,6 +40,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
|
||||||
class BankingService(
|
class BankingService(
|
||||||
private val uiState: UiState,
|
private val uiState: UiState,
|
||||||
private val uiSettings: UiSettings,
|
private val uiSettings: UiSettings,
|
||||||
|
private val uiService: UiService,
|
||||||
private val bankingRepository: BankingRepository,
|
private val bankingRepository: BankingRepository,
|
||||||
private val bankFinder: BankFinder
|
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()
|
fun getAllBanks() = bankingRepository.getAllBanks()
|
||||||
|
|
||||||
suspend fun updateBank(bank: BankAccessEntity, loginName: String, password: String, bankName: String?) {
|
suspend fun updateBank(bank: BankAccessEntity, loginName: String, password: String, bankName: String?) {
|
||||||
|
|
|
@ -4,9 +4,9 @@ import kotlinx.coroutines.*
|
||||||
|
|
||||||
class UiService {
|
class UiService {
|
||||||
|
|
||||||
fun debounce(coroutineScope: CoroutineScope, action: () -> Unit): Job {
|
fun debounce(coroutineScope: CoroutineScope, delayMillis: Long = 250, action: suspend () -> Unit): Job {
|
||||||
return coroutineScope.launch {
|
return coroutineScope.launch {
|
||||||
delay(250)
|
delay(delayMillis)
|
||||||
action()
|
action()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue