commit ae24e23b5c1607523e4fdccff11a1c75d90094ed parent d45032f07e25f15870e65159c42a25ec625d1f37 Author: Bohdan Potuzhnyi <bohdan.potuzhnyi@gmail.com> Date: Sun, 7 Jun 2026 18:47:40 +0200 [merchant-terminal] #0011425 Diffstat:
62 files changed, 863 insertions(+), 339 deletions(-)
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt @@ -72,10 +72,12 @@ class MerchantApi( suspend fun deleteOrder( merchantConfig: MerchantConfig, orderId: String, + force: Boolean = false, ): Response<Unit> = withContext(ioDispatcher) { response { httpClient.delete(merchantConfig.urlFor("private/orders/$orderId")) { auth(merchantConfig) + if (force) parameter("force", "yes") }.body() } } diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt @@ -16,14 +16,15 @@ package net.taler.merchantlib -import io.ktor.client.call.body import io.ktor.client.plugins.ResponseException +import io.ktor.client.statement.bodyAsText import java.io.IOException import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException import java.nio.channels.UnresolvedAddressException import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json class Response<out T> private constructor( private val value: Any? @@ -81,8 +82,10 @@ class Response<out T> private constructor( private suspend fun getExceptionString(e: ResponseException): String { val response = e.response + val responseText = response.bodyAsText() + parseInventoryAvailabilityError(response.status.value, responseText)?.let { return it } return try { - val error: Error = response.body() + val error = Json.decodeFromString<Error>(responseText) buildString { append("Error") error.code?.let { @@ -106,6 +109,25 @@ class Response<out T> private constructor( } } + private fun parseInventoryAvailabilityError(statusCode: Int, body: String): String? { + if (statusCode != 410) return null + val error = runCatching { + Json { ignoreUnknownKeys = true } + .decodeFromString<InventoryAvailabilityError>(body) + }.getOrNull() ?: return null + val productId = error.productId?.takeIf(String::isNotBlank) ?: return null + val requested = error.unitRequestedQuantity + ?.takeIf(String::isNotBlank) + ?: error.requestedQuantity?.toString() + ?: return null + val available = error.unitAvailableQuantity + ?.takeIf(String::isNotBlank) + ?: error.availableQuantity?.toString() + ?: return null + return "Inventory stock unavailable for product $productId: " + + "$requested requested, $available available." + } + private fun fallbackStatusMessage(statusCode: Int): String = when (statusCode) { 400 -> "Bad request (400)" 401 -> "Unauthorized (401)" @@ -124,4 +146,18 @@ class Response<out T> private constructor( val hint: String?, val detail: String? = null, ) + + @Serializable + private class InventoryAvailabilityError( + @kotlinx.serialization.SerialName("product_id") + val productId: String? = null, + @kotlinx.serialization.SerialName("requested_quantity") + val requestedQuantity: Int? = null, + @kotlinx.serialization.SerialName("unit_requested_quantity") + val unitRequestedQuantity: String? = null, + @kotlinx.serialization.SerialName("available_quantity") + val availableQuantity: Int? = null, + @kotlinx.serialization.SerialName("unit_available_quantity") + val unitAvailableQuantity: String? = null, + ) } diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt @@ -17,6 +17,7 @@ package net.taler.merchantlib import io.ktor.http.HttpStatusCode.Companion.NotFound +import io.ktor.http.HttpStatusCode.Companion.Gone import java.net.UnknownHostException import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking @@ -113,6 +114,28 @@ class MerchantApiTest { assertTrue(it.contains("2000")) assertTrue(it.contains("merchant instance unknown")) } + + httpClient.giveJsonResponse( + "http://example.net/instances/testInstance/private/orders", + statusCode = Gone, + ) { + """ + { + "product_id": "finite_quantity", + "requested_quantity": 10, + "unit_requested_quantity": "10", + "available_quantity": 8, + "unit_available_quantity": "8" + } + """.trimIndent() + } + api.postOrder(merchantConfig, request).assertFailure { + assertEquals( + "Inventory stock unavailable for product finite_quantity: " + + "10 requested, 8 available.", + it, + ) + } } @Test diff --git a/merchant-terminal/src/main/AndroidManifest.xml b/merchant-terminal/src/main/AndroidManifest.xml @@ -33,9 +33,9 @@ <application android:allowBackup="true" android:fullBackupContent="@xml/backup_descriptor" - android:icon="@mipmap/ic_taler_logo" + android:icon="@mipmap/ic_taler_pos_logo" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_taler_logo_round" + android:roundIcon="@mipmap/ic_taler_pos_logo_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:enableOnBackInvokedCallback="false" diff --git a/merchant-terminal/src/main/ic_taler_logo-playstore.png b/merchant-terminal/src/main/ic_taler_logo-playstore.png Binary files differ. diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt @@ -46,6 +46,7 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.Image @@ -313,6 +314,8 @@ private fun MerchantTerminalApp( val currentOrderState = currentOrderLive?.order?.observeAsState() val currentOrder = currentOrderState?.value val restartState by currentOrderLive?.restartState?.observeAsState(DISABLED) ?: remember { androidx.compose.runtime.mutableStateOf(DISABLED) } + val clearOrderEnabled = + restartState == UNDO || currentOrder?.products?.isNotEmpty() == true val hasPreviousOrder = currentOrderId?.let { viewModel.orderManager.hasPreviousOrder(it) } ?: false val hasNextOrder by currentOrderId?.let { viewModel.orderManager.hasNextOrder(it).observeAsState(false) } ?: remember { androidx.compose.runtime.mutableStateOf(false) } @@ -429,7 +432,7 @@ private fun MerchantTerminalApp( Icon(Icons.Default.Menu, contentDescription = null) } } else { - Spacer(modifier = Modifier.width(48.dp)) + Spacer(modifier = Modifier.size(48.dp)) } Text( text = screenTitle, @@ -442,7 +445,7 @@ private fun MerchantTerminalApp( ) { Button( onClick = { currentOrderLive?.restartOrUndo() }, - enabled = restartState != DISABLED, + enabled = clearOrderEnabled, colors = topBarOrderButtonColors(), modifier = Modifier.heightIn(min = 40.dp), ) { @@ -502,7 +505,7 @@ private fun MerchantTerminalApp( Icon(Icons.Default.Menu, contentDescription = null) } } else { - Spacer(modifier = Modifier.width(48.dp)) + Spacer(modifier = Modifier.size(48.dp)) } Text( text = screenTitle, @@ -533,7 +536,7 @@ private fun MerchantTerminalApp( Icon(Icons.Default.Menu, contentDescription = null) } } else { - Spacer(modifier = Modifier.width(48.dp)) + Spacer(modifier = Modifier.size(48.dp)) } Text( text = screenTitle, diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/amount/AmountEntryFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/amount/AmountEntryFragment.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenuItem @@ -46,6 +47,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -316,6 +318,20 @@ private fun AmountPane( singleLine = true, label = { Text(stringResource(R.string.amount_entry_label)) }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + shape = RoundedCornerShape(14.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLow, + unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLow, + disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerLow, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant, + focusedLabelColor = MaterialTheme.colorScheme.primary, + unfocusedLabelColor = MaterialTheme.colorScheme.onSurfaceVariant, + focusedTextColor = MaterialTheme.colorScheme.onSurface, + unfocusedTextColor = MaterialTheme.colorScheme.onSurface, + focusedTrailingIconColor = MaterialTheme.colorScheme.primary, + unfocusedTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, + ), ) DropdownMenu( diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt @@ -45,6 +45,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.SerialDescriptor @@ -126,6 +127,18 @@ private data class MerchantBackendConfigResponse( ) @Serializable +private data class ProductDetail( + @SerialName("total_sold") + val totalSold: Int? = null, + @SerialName("total_lost") + val totalLost: Int? = null, + @SerialName("unit_total_sold") + val unitTotalSold: String? = null, + @SerialName("unit_total_lost") + val unitTotalLost: String? = null, +) + +@Serializable private data class CachedRuntimeConfig( val posConfig: PosConfig, val merchantConfig: MerchantConfig, @@ -383,12 +396,13 @@ class ConfigManager( return } val currencySpec = configResponse.currencies[configResponse.currency] + val posConfigWithCachedStock = applyCachedStockData(posConfig) for (receiver in configurationReceivers) { val result = try { if (inventoryOnly) { - receiver.onInventoryUpdated(posConfig, configResponse.currency, currencySpec) + receiver.onInventoryUpdated(posConfigWithCachedStock, configResponse.currency, currencySpec) } else { - receiver.onConfigurationReceived(posConfig, configResponse.currency, currencySpec) + receiver.onConfigurationReceived(posConfigWithCachedStock, configResponse.currency, currencySpec) } } catch (e: Exception) { Log.e(TAG, "Error handling configuration by ${receiver::class.java.simpleName}", e) @@ -409,7 +423,7 @@ class ConfigManager( this@ConfigManager.currencySpec = currencySpec saveCachedRuntimeConfig( CachedRuntimeConfig( - posConfig = posConfig, + posConfig = posConfigWithCachedStock, merchantConfig = merchantConfig, currency = configResponse.currency, currencySpec = currencySpec, @@ -419,11 +433,82 @@ class ConfigManager( mConfigUpdateResult.value = ConfigUpdateResult.Success(configResponse.currency) } } + scope.launch(Dispatchers.IO) { + val enrichedPosConfig = enrichProductsWithStockDetails(posConfig, merchantConfig) + if (enrichedPosConfig !== posConfig) { + for (receiver in configurationReceivers) { + try { + receiver.onInventoryUpdated(enrichedPosConfig, configResponse.currency, currencySpec) + } catch (e: Exception) { + Log.e(TAG, "Error updating stock for ${receiver::class.java.simpleName}", e) + } + } + saveCachedRuntimeConfig( + CachedRuntimeConfig( + posConfig = enrichedPosConfig, + merchantConfig = merchantConfig, + currency = configResponse.currency, + currencySpec = currencySpec, + ) + ) + } + } + } + + private fun applyCachedStockData(posConfig: PosConfig): PosConfig { + val cached = cachedRuntimeConfig ?: return posConfig + val cachedByProductId = cached.posConfig.products + .filter { it.productId != null } + .associateBy { it.productId } + var changed = false + val products = posConfig.products.map { product -> + val cachedProduct = product.productId?.let { cachedByProductId[it] } + ?: return@map product + if (cachedProduct.totalSold == null && cachedProduct.totalLost == null && + cachedProduct.unitTotalSold == null && cachedProduct.unitTotalLost == null + ) return@map product + changed = true + product.copy( + totalSold = cachedProduct.totalSold, + totalLost = cachedProduct.totalLost, + unitTotalSold = cachedProduct.unitTotalSold, + unitTotalLost = cachedProduct.unitTotalLost, + ) + } + return if (changed) posConfig.copy(products = products) else posConfig + } + + @WorkerThread + private suspend fun enrichProductsWithStockDetails( + posConfig: PosConfig, + merchantConfig: MerchantConfig, + ): PosConfig { + val enrichedProducts = posConfig.products.map { product -> + val pid = product.productId ?: return@map product + if (product.stockLimit == null) return@map product + try { + val url = merchantConfig.urlFor("private/products/" + pid) + val detail: ProductDetail = httpClient.get(url) { + header(Authorization, "Bearer " + merchantConfig.apiKey) + }.body() + product.copy( + totalLost = detail.totalLost ?: product.totalLost, + totalSold = detail.totalSold ?: product.totalSold, + unitTotalLost = detail.unitTotalLost ?: product.unitTotalLost, + unitTotalSold = detail.unitTotalSold ?: product.unitTotalSold, + ) + } catch (e: Exception) { + Log.w(TAG, "Failed to fetch stock details for " + pid, e) + product + } + } + if (enrichedProducts == posConfig.products) return posConfig + return posConfig.copy(products = enrichedProducts) } /** * POSTs to /instances/{username}/private/token with the user’s raw secret, - * returns the new “write” token (without the “secret-token:” prefix). + * returns the new "write" token (without the "secret-token:" prefix). */ @WorkerThread suspend fun fetchLimitedAccessToken( diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/PosConfig.kt @@ -108,8 +108,17 @@ data class ConfigProduct( val totalStock: Int? = null, @SerialName("unit_total_stock") val unitTotalStock: String? = null, + @SerialName("total_sold") + val totalSold: Int? = null, + @SerialName("unit_total_sold") + val unitTotalSold: String? = null, + @SerialName("total_lost") + val totalLost: Int? = null, + @SerialName("unit_total_lost") + val unitTotalLost: String? = null, val availableToSell: Boolean = true, val remainingStock: Int? = null, + val currencyMismatch: Boolean = false, ) : OrderProduct() { val totalPrice: Amount get() = (price * quantity).withSpec(price.spec) @@ -138,11 +147,22 @@ data class ConfigProduct( val stockLimit: Int? get() { if (totalStock == -1 || unitTotalStock == "-1") return null - totalStock?.let { return it } - val decimalStock = unitTotalStock ?: return null - return decimalStock.toBigDecimalOrNull() - ?.setScale(0, RoundingMode.DOWN) - ?.toInt() + val total = totalStock + ?: unitTotalStock?.toBigDecimalOrNull() + ?.setScale(0, RoundingMode.DOWN) + ?.toInt() + ?: return null + val sold = totalSold + ?: unitTotalSold?.toBigDecimalOrNull() + ?.setScale(0, RoundingMode.DOWN) + ?.toInt() + ?: 0 + val lost = totalLost + ?: unitTotalLost?.toBigDecimalOrNull() + ?.setScale(0, RoundingMode.DOWN) + ?.toInt() + ?: 0 + return (total - sold - lost).coerceAtLeast(0) } fun toContractProduct() = ContractProduct( diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt @@ -28,7 +28,9 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme @@ -84,6 +86,7 @@ class HistoryFragment : Fragment(), HistoryActionListener { val result by historyManager.items.observeAsState() val pendingRefundOrderId by refundManager.pendingRefundOrderId.observeAsState() val activePayment by model.paymentManager.payment.observeAsState() + val forceDeleteOrderId by historyManager.forceDeleteOrderId.observeAsState() HistoryScreen( isLoading = isLoading, isLoadingMore = isLoadingMore, @@ -97,6 +100,12 @@ class HistoryFragment : Fragment(), HistoryActionListener { onShowPaymentClicked = ::onShowPaymentClicked, onShowRefundClicked = ::onShowRefundClicked, ) + forceDeleteOrderId?.let { orderId -> + ForceDeleteOrderDialog( + onConfirm = { historyManager.forceDeleteOrder(orderId) }, + onDismiss = { historyManager.clearForceDeletePrompt() }, + ) + } } } @@ -366,3 +375,30 @@ private fun HistoryAmountBadge( ) } } + +@Composable +private fun ForceDeleteOrderDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(stringResource(R.string.force_delete_dialog_title)) }, + text = { Text(stringResource(R.string.force_delete_dialog_message)) }, + confirmButton = { + Button( + onClick = onConfirm, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.error, + ), + ) { + Text(stringResource(R.string.force_delete_dialog_confirm)) + } + }, + dismissButton = { + OutlinedButton(onClick = onDismiss) { + Text(stringResource(R.string.payment_cancel)) + } + }, + ) +} diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryManager.kt @@ -111,12 +111,19 @@ class HistoryManager( } } + private val mForceDeleteOrderId = MutableLiveData<String?>(null) + val forceDeleteOrderId: LiveData<String?> = mForceDeleteOrderId + @UiThread internal fun deleteOrder(orderId: String) = scope.launch { mIsLoading.value = true mIsLoadingMore.value = false val merchantConfig = configManager.merchantConfig!! - api.deleteOrder(merchantConfig, orderId).handle({ onError(R.string.error_delete_order, it) }) { + api.deleteOrder(merchantConfig, orderId).handle({ errorMsg -> + assertUiThread() + mIsLoading.value = false + mForceDeleteOrderId.value = orderId + }) { assertUiThread() configManager.refreshInventory() fetchHistory() @@ -124,6 +131,25 @@ class HistoryManager( } @UiThread + internal fun forceDeleteOrder(orderId: String) = scope.launch { + mForceDeleteOrderId.postValue(null) + mIsLoading.value = true + val merchantConfig = configManager.merchantConfig!! + api.deleteOrder(merchantConfig, orderId, force = true).handle({ errorMsg -> + onError(R.string.error_delete_order, errorMsg) + }) { + assertUiThread() + configManager.refreshInventory() + fetchHistory() + } + } + + @UiThread + internal fun clearForceDeletePrompt() { + mForceDeleteOrderId.value = null + } + + @UiThread internal fun clearError() { mError.value = null } diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderFragment.kt @@ -85,6 +85,7 @@ import net.taler.merchantpos.compose.PosTheme import net.taler.merchantpos.config.Category import net.taler.merchantpos.config.ConfigProduct import net.taler.merchantpos.showPosError +import kotlinx.coroutines.delay import net.taler.merchantpos.order.RestartState.DISABLED import androidx.compose.foundation.shape.RoundedCornerShape @@ -154,6 +155,13 @@ private fun OrderRoute( var showCustomDialog by rememberSaveable { mutableStateOf(false) } val reloadingText = stringResource(R.string.toast_reloading) + LaunchedEffect(Unit) { + while (true) { + delay(30_000) + viewModel.configManager.refreshInventory() + } + } + LaunchedEffect(order?.products, liveOrder.lastAddedProduct?.id) { val productsInOrder = order?.products.orEmpty() val selected = selectedProductKey?.let { key -> productsInOrder.find { it.id == key } } @@ -212,7 +220,6 @@ private fun OrderRoute( }, onProductSelected = { product -> orderManager.addProduct(orderId, product) - viewModel.configManager.refreshInventory() }, onSelectProduct = { selectedProductKey = it?.id @@ -243,7 +250,6 @@ private fun OrderRoute( }, onProductSelected = { product -> orderManager.addProduct(orderId, product) - viewModel.configManager.refreshInventory() }, onSelectProduct = { selectedProductKey = it?.id @@ -600,10 +606,10 @@ private fun ProductCard( ) if (!product.availableToSell) { Text( - text = if (product.remainingStock == 0) { - stringResource(R.string.product_out_of_stock) - } else { - stringResource(R.string.product_unavailable) + text = when { + product.currencyMismatch -> stringResource(R.string.product_wrong_currency) + product.remainingStock == 0 -> stringResource(R.string.product_out_of_stock) + else -> stringResource(R.string.product_unavailable) }, color = MaterialTheme.colorScheme.error, style = if (compact) MaterialTheme.typography.labelSmall else MaterialTheme.typography.bodySmall, diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderManager.kt @@ -106,18 +106,17 @@ class OrderManager(private val context: Context) : ConfigurationReceiver { posConfig.products.forEach { product -> val productCurrency = product.price.currency - if (productCurrency != currency) { - Log.e(TAG, "Product $product has currency $productCurrency, $currency expected") - return context.getString( - R.string.config_error_currency, product.description, productCurrency, currency - ) + val currencyMismatch = productCurrency != currency + if (currencyMismatch) { + Log.w(TAG, "Product $product has currency $productCurrency, $currency expected") } val remainingStock = product.stockLimit val productWithSpec = product.copy( id = existingProductsByStableKey[product.stableKey]?.id ?: product.id, price = product.price.withSpec(currencySpec), - availableToSell = remainingStock == null || remainingStock > 0, + availableToSell = !currencyMismatch && (remainingStock == null || remainingStock > 0), remainingStock = remainingStock, + currencyMismatch = currencyMismatch, ) productsById[productWithSpec.id] = productWithSpec productsByCategory.getValue(allProductsCategory).add(productWithSpec) @@ -162,6 +161,8 @@ class OrderManager(private val context: Context) : ConfigurationReceiver { orderCounter = 0 orders[0] = createOrder(0) mCurrentOrderId.postValue(0) + } else { + trimOrdersToStockLimits() } return null } @@ -313,7 +314,7 @@ class OrderManager(private val context: Context) : ConfigurationReceiver { private fun decorateProduct(product: ConfigProduct): ConfigProduct { val remainingStock = remainingStock(product) return product.copy( - availableToSell = remainingStock == null || remainingStock > 0, + availableToSell = !product.currencyMismatch && (remainingStock == null || remainingStock > 0), remainingStock = remainingStock, ) } @@ -333,4 +334,21 @@ class OrderManager(private val context: Context) : ConfigurationReceiver { } return (stockLimit - reserved).coerceAtLeast(0) } + + private fun trimOrdersToStockLimits() { + for (liveOrder in orders.values) { + val order = liveOrder.order.value ?: continue + var modified = false + val trimmedProducts = order.products.mapNotNull { orderProduct -> + val stockLimit = productsById[orderProduct.id]?.stockLimit ?: return@mapNotNull orderProduct + if (orderProduct.quantity <= stockLimit) return@mapNotNull orderProduct + modified = true + if (stockLimit <= 0) null + else orderProduct.copy(quantity = stockLimit) + } + if (modified) { + liveOrder.order.postValue(order.copy(products = trimmedProducts)) + } + } + } } diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt @@ -195,6 +195,7 @@ class PaymentManager( @UiThread fun cancelPayment(error: String? = null) { + stopPaymentChecks() val merchantConfig = configManager.merchantConfig!! mPayment.value?.let { payment -> if (!payment.paid) payment.orderId?.let { orderId -> @@ -207,6 +208,52 @@ class PaymentManager( mPayment.value?.copy(error = error)?.let { mPayment.value = it } + } + + private val mDeleteNeedsForce = MutableLiveData<Boolean?>(null) + val deleteNeedsForce: LiveData<Boolean?> = mDeleteNeedsForce + + @UiThread + fun tryDeleteOrder() { + stopPaymentChecks() + val merchantConfig = configManager.merchantConfig!! + val orderId = mPayment.value?.orderId ?: return + scope.launch { + val result = api.deleteOrder(merchantConfig, orderId) + result.handle( + onFailure = { errorStr -> + Log.w(TAG, "Delete order $orderId failed: $errorStr") + mDeleteNeedsForce.postValue(true) + }, + onSuccess = { + Log.d(TAG, "Delete order $orderId succeeded") + mPayment.postValue(mPayment.value?.copy(error = null)) + mDeleteNeedsForce.postValue(false) + }, + ) + } + } + + @UiThread + fun forceDeleteOrder() { + val merchantConfig = configManager.merchantConfig!! + val orderId = mPayment.value?.orderId ?: return + Log.d(TAG, "Force deleting claimed order $orderId") + scope.launch { + api.deleteOrder(merchantConfig, orderId, force = true).handle( + onFailure = { err -> Log.w(TAG, "Force delete of order $orderId failed: $err") }, + onSuccess = { Log.d(TAG, "Force delete of order $orderId succeeded") }, + ) + } + mPayment.value = mPayment.value?.copy(error = null) + mDeleteNeedsForce.value = null + } + + fun clearDeleteNeedsForce() { + mDeleteNeedsForce.value = null + } + + private fun stopPaymentChecks() { checkTimer.cancel() checkJob?.isCancelled checkJob = null diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt @@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator @@ -37,7 +38,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView @@ -76,17 +81,37 @@ class ProcessPaymentFragment : Fragment() { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { val payment by paymentManager.payment.observeAsState() + val deleteNeedsForce by paymentManager.deleteNeedsForce.observeAsState() payment?.let { ProcessPaymentScreen( payment = it, deviceHasNfc = deviceHasNfc, - onCancel = ::onPaymentCancel, + showForceDeleteDialog = deleteNeedsForce == true, + onCancel = { + if (it.claimed) { + paymentManager.tryDeleteOrder() + } else { + onPaymentCancel() + } + }, + onForceDelete = ::onForceDeleteOrder, + onDismissForceDelete = { + paymentManager.clearDeleteNeedsForce() + }, onShare = { uri -> requireContext().shareText(uri) }, onCopy = { uri -> copyToClipBoard(requireContext(), "Payment URI", uri) }, ) } + + // Navigate back on successful (non-force) delete + LaunchedEffect(deleteNeedsForce) { + if (deleteNeedsForce == false) { + paymentManager.clearDeleteNeedsForce() + (requireActivity() as MainActivity).navigateBack() + } + } } } @@ -119,6 +144,11 @@ class ProcessPaymentFragment : Fragment() { (requireActivity() as MainActivity).navigateBack() } + private fun onForceDeleteOrder() { + paymentManager.forceDeleteOrder() + (requireActivity() as MainActivity).navigateBack() + } + private fun getPaymentErrorDisplay(payment: Payment): Pair<String, String> { val error = payment.error.orEmpty() if (payment.orderId != null) { @@ -143,10 +173,36 @@ class ProcessPaymentFragment : Fragment() { private fun ProcessPaymentScreen( payment: Payment, deviceHasNfc: Boolean, + showForceDeleteDialog: Boolean, onCancel: () -> Unit, + onForceDelete: () -> Unit, + onDismissForceDelete: () -> Unit, onShare: (String) -> Unit, onCopy: (String) -> Unit, ) { + if (showForceDeleteDialog) { + AlertDialog( + onDismissRequest = onDismissForceDelete, + title = { Text(stringResource(R.string.force_delete_dialog_title)) }, + text = { Text(stringResource(R.string.force_delete_dialog_message)) }, + confirmButton = { + Button( + onClick = onForceDelete, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.error, + ), + ) { + Text(stringResource(R.string.force_delete_dialog_confirm)) + } + }, + dismissButton = { + OutlinedButton(onClick = onDismissForceDelete) { + Text(stringResource(R.string.payment_cancel)) + } + }, + ) + } + PosTheme { val introText = if (payment.claimed) { stringResource(R.string.payment_claimed) @@ -193,32 +249,34 @@ private fun TabletProcessPaymentScreen( .weight(1f), contentAlignment = Alignment.Center, ) { - BoxWithConstraints( - modifier = Modifier - .fillMaxSize() - .padding(12.dp), - contentAlignment = Alignment.Center, - ) { - val payUri = payment.talerPayUri - val qrSize = minOf(maxWidth, maxHeight) - Box( - modifier = Modifier.size(qrSize), + if (!payment.claimed) { + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .padding(12.dp), contentAlignment = Alignment.Center, ) { - if (payUri == null) { - CircularProgressIndicator() - } else { - AnimatedQrCodeComposable( - link = payUri, - logoPainter = painterResource(R.drawable.ic_taler_logo_qr), - modifier = Modifier.fillMaxSize(), - ) + val payUri = payment.talerPayUri + val qrSize = minOf(maxWidth, maxHeight) + Box( + modifier = Modifier.size(qrSize), + contentAlignment = Alignment.Center, + ) { + if (payUri == null) { + CircularProgressIndicator() + } else { + AnimatedQrCodeComposable( + link = payUri, + logoPainter = painterResource(R.drawable.ic_taler_logo_qr), + modifier = Modifier.fillMaxSize(), + ) + } } } } } - payment.talerPayUri?.let { payUri -> + if (!payment.claimed) payment.talerPayUri?.let { payUri -> Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -295,32 +353,34 @@ private fun PhoneProcessPaymentScreen( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - contentAlignment = Alignment.Center, - ) { - BoxWithConstraints( + if (!payment.claimed) { + Box( modifier = Modifier - .fillMaxSize() - .padding(12.dp), + .fillMaxWidth() + .weight(1f), contentAlignment = Alignment.Center, ) { - val payUri = payment.talerPayUri - val qrSize = minOf(maxWidth, maxHeight) - Box( - modifier = Modifier.size(qrSize), + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + .padding(12.dp), contentAlignment = Alignment.Center, ) { - if (payUri == null) { - CircularProgressIndicator() - } else { - AnimatedQrCodeComposable( - link = payUri, - logoPainter = painterResource(R.drawable.ic_taler_logo_qr), - modifier = Modifier.fillMaxSize(), - ) + val payUri = payment.talerPayUri + val qrSize = minOf(maxWidth, maxHeight) + Box( + modifier = Modifier.size(qrSize), + contentAlignment = Alignment.Center, + ) { + if (payUri == null) { + CircularProgressIndicator() + } else { + AnimatedQrCodeComposable( + link = payUri, + logoPainter = painterResource(R.drawable.ic_taler_logo_qr), + modifier = Modifier.fillMaxSize(), + ) + } } } } diff --git a/merchant-terminal/src/main/res/drawable/ic_launcher_background.xml b/merchant-terminal/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="108dp" - android:height="108dp" - android:viewportWidth="108" - android:viewportHeight="108"> - <path - android:fillColor="#008577" - android:pathData="M0,0h108v108h-108z" /> - <path - android:fillColor="#00000000" - android:pathData="M9,0L9,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,0L19,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,0L29,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,0L39,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,0L49,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,0L59,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,0L69,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,0L79,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M89,0L89,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M99,0L99,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,9L108,9" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,19L108,19" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,29L108,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,39L108,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,49L108,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,59L108,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,69L108,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,79L108,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,89L108,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,99L108,99" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,29L89,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,39L89,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,49L89,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,59L89,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,69L89,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,79L89,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,19L29,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,19L39,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,19L49,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,19L59,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,19L69,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,19L79,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> -</vector> diff --git a/merchant-terminal/src/main/res/drawable/ic_taler_pos_logo_foreground.xml b/merchant-terminal/src/main/res/drawable/ic_taler_pos_logo_foreground.xml @@ -0,0 +1,41 @@ +<!-- + ~ Copyright (C) 2026 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + <group android:scaleX="0.6" + android:scaleY="0.6" + android:translateX="204.8" + android:translateY="204.8"> + <path + android:pathData="M221.1,890.8V669.2H313C322.5,669.2 331.3,671.2 339.2,675.1C347.3,679.1 354.3,684.5 360.1,691.3C365.9,698 370.5,705.5 373.9,713.8C377.2,721.9 378.9,730.2 378.9,738.8C378.9,750.8 376.1,762.3 370.7,773.1C365.5,783.7 358.1,792.3 348.6,799C339,805.7 327.7,809 314.8,809H243V890.8H221.1ZM243,789.6H313.9C322.6,789.6 330.2,787.3 336.7,782.5C343.1,777.7 348.1,771.4 351.7,763.7C355.2,756 357,747.7 357,738.8C357,729.6 354.9,721.2 350.7,713.5C346.6,705.8 341.1,699.8 334.2,695.4C327.5,690.8 320.1,688.5 312,688.5H243V789.6ZM504.7,892.3C489.1,892.3 475,889.2 462.3,883C449.6,876.7 438.6,868.3 429.5,857.7C420.3,846.9 413.2,834.8 408.2,821.5C403.2,808 400.7,794.1 400.7,780C400.7,765.2 403.3,751.2 408.5,737.8C413.8,724.3 421,712.4 430.4,702C440,691.3 451.1,683 463.8,677C476.5,670.7 490.3,667.6 505.1,667.6C520.7,667.6 534.8,670.8 547.5,677.3C560.2,683.7 571.1,692.4 580,703.2C589.2,714 596.3,726.1 601.3,739.4C606.2,752.7 608.7,766.3 608.7,780.3C608.7,795.1 606.1,809.2 600.9,822.7C595.7,836 588.4,848 579.1,858.6C569.7,869 558.7,877.2 546,883.3C533.3,889.3 519.5,892.3 504.7,892.3ZM422.6,780C422.6,792 424.6,803.7 428.5,814.9C432.5,825.9 438.1,835.8 445.4,844.6C452.7,853.1 461.3,860 471.3,865.2C481.5,870.2 492.7,872.7 504.7,872.7C517.4,872.7 528.8,870.1 538.8,864.9C549,859.4 557.6,852.4 564.7,843.6C572,834.7 577.5,824.7 581.3,813.7C585,802.6 586.9,791.4 586.9,780C586.9,767.9 584.9,756.4 581,745.3C577,734.3 571.3,724.4 563.8,715.7C556.5,706.9 547.8,700.1 537.8,695.1C527.9,690.1 516.8,687.6 504.7,687.6C492.3,687.6 480.9,690.2 470.7,695.4C460.7,700.6 452.1,707.7 444.8,716.6C437.7,725.4 432.2,735.2 428.2,746.3C424.5,757.3 422.6,768.5 422.6,780ZM779.6,710.7C776.5,707.2 772.8,704 768.6,701.3C764.5,698.4 759.8,695.9 754.6,693.8C749.4,691.8 743.8,690.1 737.7,688.8C731.9,687.6 725.7,687 719,687C699.6,687 685.4,690.7 676.2,698.2C667.2,705.5 662.8,715.5 662.8,728.2C662.8,736.9 664.9,743.8 669,748.8C673.4,753.8 680.2,757.8 689.3,760.9C698.5,764.1 710.1,767.3 724.3,770.6C740.1,773.9 753.8,777.9 765.2,782.5C776.7,787 785.5,793.3 791.8,801.2C798,808.9 801.1,819.4 801.1,832.7C801.1,842.9 799.2,851.7 795.2,859.2C791.2,866.7 785.7,873 778.6,878C771.6,883 763.1,886.7 753.3,889.2C743.6,891.5 732.8,892.6 721.2,892.6C709.7,892.6 698.7,891.5 688.1,889.2C677.7,886.7 667.8,883.2 658.4,878.6C649,873.8 640.3,867.8 632.2,860.5L643.1,842.7C647,846.9 651.7,850.8 657.2,854.6C662.8,858.1 668.9,861.3 675.6,864.2C682.4,867.1 689.7,869.4 697.4,871.1C705.4,872.5 713.5,873.3 721.8,873.3C739.5,873.3 753.2,870.1 763,863.6C773,857.2 778,847.6 778,834.9C778,825.7 775.5,818.5 770.5,813C765.5,807.4 758,802.9 748,799.3C738,795.8 725.8,792.3 711.2,789C695.8,785.5 682.8,781.5 672.1,777.2C661.5,772.8 653.5,767.1 648.1,760C642.9,752.7 640.3,743.1 640.3,731.3C640.3,717.6 643.6,706 650.3,696.6C657.2,687.1 666.5,679.9 678.4,675.1C690.3,670.1 703.9,667.6 719.3,667.6C729.1,667.6 738.1,668.7 746.5,670.7C755,672.6 762.8,675.4 769.9,679.2C777.2,682.9 784,687.6 790.2,693.2L779.6,710.7Z" + android:fillColor="#000000"/> + <path + android:pathData="M617,358.5C617,373.9 614.2,388.8 613.3,395.2C612.3,401.7 611.1,408 609.7,414.3C608.3,420.6 606.6,426.9 604.7,433.1C602.9,439.3 600.7,445.4 598.4,451.4C596.1,457.4 593.5,463.3 590.8,469.1C588,474.9 585,480.6 581.8,486.1C578.7,491.7 575.2,497 571.7,502.3C568,507.6 564.3,512.7 560.3,517.6C556.4,522.6 552.2,527.4 547.9,532C543.6,536.6 539.1,540.9 534.5,545.2H534.4C533.4,546.1 532.3,547 531.2,547.9C529.9,549.1 528.5,550.2 527.2,551.4C524.9,553.4 522.5,555.4 520.1,557.3C516.4,560.1 512.7,562.8 508.9,565.4C507.6,566.3 506.2,567.2 504.9,568.1C500.6,570.9 496.2,573.6 491.8,576.1C490.8,576.6 489.9,577.1 488.9,577.6C483.5,580.6 478,583.3 472.4,585.8C466.7,588.3 460.9,590.5 455.1,592.5C450.3,594.2 445.4,595.7 440.5,597.1C439.5,597.3 438.5,597.6 437.5,597.8C431.6,599.4 425.6,600.6 419.6,601.6C415.4,602.3 411.2,602.9 407,603.3C405.2,603.6 403.3,603.8 401.4,603.9C395.5,604.4 389.4,604.7 383.4,604.7H383.2C381.1,604.7 379.1,604.6 376.9,604.6C374.9,604.5 372.8,604.5 370.7,604.4C368.7,604.2 366.6,604.1 364.6,603.9C362.5,603.7 360.5,603.5 358.4,603.3C356.4,603 354.3,602.7 352.2,602.5C350.2,602.2 348.2,601.9 346.1,601.5C344.1,601.2 342,600.8 340,600.4C338,600 335.9,599.5 333.9,599.1C331.9,598.6 329.9,598.1 327.9,597.6C325.9,597.1 323.8,596.5 321.9,595.9C319.9,595.3 317.9,594.7 315.9,594.1C313.9,593.5 312,592.8 310,592.1C308,591.4 306.1,590.7 304.1,590C302.2,589.2 300.3,588.4 298.3,587.6C296.4,586.8 294.5,586 292.6,585.1C290.7,584.3 288.8,583.4 286.9,582.5C285,581.6 283.1,580.7 281.3,579.7C280.3,579.2 279.3,578.6 278.3,578.1C277.5,577.6 276.6,577.2 275.7,576.7C273.9,575.7 272.1,574.6 270.3,573.6C268.5,572.5 266.7,571.4 264.9,570.3C263.1,569.2 261.3,568 259.6,566.8C257.9,565.7 256.1,564.5 254.4,563.3C252.6,562.1 251,560.8 249.3,559.6C247.6,558.3 245.9,557 244.2,555.7C242.6,554.4 240.9,553 239.3,551.7C237.7,550.3 236.1,548.9 234.5,547.5C232.9,546.1 231.3,544.7 229.8,543.2C228.2,541.8 226.7,540.3 225.2,538.9C223.6,537.3 222.1,535.8 220.6,534.3C219.1,532.7 217.7,531.2 216.3,529.6C214.8,528 213.4,526.4 212,524.8C210.6,523.2 209.2,521.5 207.9,519.9C206.5,518.2 205.2,516.5 203.8,514.9C202.5,513.2 201.2,511.4 199.9,509.7C198.6,508 197.4,506.2 196.2,504.4C195,502.7 193.7,500.9 192.5,499.1C191.3,497.3 190.2,495.5 189,493.7C187.9,491.8 186.8,489.9 185.7,488.1C184.6,486.2 183.5,484.3 182.5,482.4C181.4,480.5 180.4,478.6 179.4,476.7C178.4,474.8 177.4,472.8 176.4,470.9C175.5,468.9 174.5,466.9 173.7,465C172.8,463 171.8,461 171,459C170.2,457 169.3,454.9 168.5,452.9C167.7,450.9 167,448.9 166.2,446.8C165.4,444.7 164.7,442.7 164,440.6C163.3,438.5 162.6,436.5 162,434.4C161.4,432.3 160.7,430.2 160.1,428C159.5,425.9 158.9,423.9 158.4,421.7C157.9,419.6 157.3,417.4 156.8,415.3C156.4,413.2 155.9,411 155.4,408.9C155,406.7 154.6,404.5 154.2,402.4C153.8,400.2 153.5,398 153.2,395.9C152.8,393.7 152.5,391.5 152.2,389.3C152,387.1 151.7,385 151.5,382.8C151.3,380.6 151.1,378.4 150.9,376.2C150.8,374.2 150.2,367 150.2,365.4C150.1,363.1 150,360.8 150,358.5C150,353.4 150.2,348.3 150.5,343.3C152.5,307.7 161.6,273.2 176.8,242C177.1,241.5 177.3,241 177.5,240.5C180.1,235.3 182.9,230.3 185.7,225.3C188.5,220.6 191.5,215.9 194.6,211.4H194.7C197.5,207.1 200.6,202.9 203.8,198.9C206.3,195.6 208.9,192.4 211.5,189.3C211.9,188.9 212.3,188.5 212.7,188C214.6,185.8 216.4,183.5 218.4,181.4C236.1,162.4 256.4,146.8 278.3,135C287.2,130.1 296.2,125.9 305.6,122.4C306.7,121.9 307.9,121.6 309,121.2C327.6,114.4 347.1,110.2 367,108.7C372.5,108.3 378,108 383.5,108C386.3,108 389,108.1 391.8,108.1L390.8,108.7L390.6,108.8C374.3,117.7 358.8,128.3 344.4,140.7C332.3,143.4 320.5,147.3 309,152.3C307.9,152.9 306.7,153.3 305.6,153.8C296.2,158.2 287,163.2 278.3,169.1C264.8,178.1 252.1,188.8 240.8,201.1L240,201.9C237.7,204.4 235.5,207 233.3,209.6C230.8,212.5 228.4,215.5 226,218.5C224.4,220.7 222.8,222.9 221.2,225.1C221,225.4 220.8,225.6 220.6,225.9C219.8,227.1 218.9,228.2 218.1,229.4C215,233.9 212.2,238.5 209.5,243.2C206.7,248.1 204.1,253.1 201.7,258.2C199.2,263.3 197,268.5 195,273.9C192.9,279.1 191,284.5 189.4,290C187.7,295.5 186.2,301 185,306.6C183.8,312.2 182.7,317.8 181.9,323.5C181,329.2 180.4,334.8 180,340.6C179.9,341.5 179.9,342.4 179.8,343.3C179.5,348.1 179.4,353 179.4,357.8C179.4,359.7 179.4,361.7 179.4,363.6C179.4,365.6 179.5,367.5 179.6,369.5V370.3C179.8,372 179.9,373.7 180,375.3C180.2,377.2 180.3,379.2 180.5,381.1C180.7,383.1 180.9,385 181.1,386.9C181.4,388.9 181.6,390.8 181.9,392.7C182.2,394.6 182.6,396.6 182.9,398.4C183.3,400.3 183.6,402.3 183.9,404.2C184.3,406.1 184.7,408 185.2,409.9C185.6,411.8 186.1,413.6 186.6,415.5C187,417.4 187.6,419.3 188.1,421.1C188.6,423 189.1,424.8 189.7,426.7C190.3,428.6 190.9,430.4 191.5,432.2C192.1,434.1 192.7,435.8 193.4,437.7C194,439.5 194.7,441.3 195.5,443.1C196.2,444.9 196.9,446.7 197.6,448.5C198.4,450.2 199.1,452 199.9,453.7C200.7,455.5 201.5,457.2 202.4,459C203.2,460.7 204.1,462.4 205,464.1C205.8,465.8 206.8,467.5 207.7,469.2C208.6,470.8 209.5,472.5 210.5,474.2C211.5,475.8 212.4,477.4 213.5,479.1C214.4,480.7 215.5,482.3 216.5,483.9C217.5,485.5 218.6,487.1 219.7,488.6C220.8,490.2 221.9,491.7 223,493.3C224.1,494.8 225.3,496.3 226.4,497.8C227.6,499.3 228.8,500.8 230,502.3C231.2,503.7 232.4,505.2 233.6,506.6C234.9,508 236.1,509.4 237.4,510.8C238.6,512.2 239.9,513.6 241.2,514.9C242.5,516.3 243.9,517.6 245.2,519C246.5,520.3 247.9,521.6 249.3,522.9C250,523.6 250.8,524.3 251.6,525C252.2,525.6 252.7,526.1 253.4,526.6C253.4,526.7 253.4,526.7 253.4,526.8C254.8,528 256.2,529.2 257.6,530.3C258.4,531 259.3,531.7 260.1,532.4C260.7,532.9 261.3,533.4 261.9,533.9C262.2,534.1 262.5,534.3 262.8,534.5C267.8,538.6 272.9,542.6 278.3,546.2C310.1,567.4 346.9,578.9 384.9,578.9C389.8,578.9 394.8,578.7 399.8,578.4C402.2,578.1 404.6,577.8 407,577.5C408.9,577.3 410.9,577.1 412.8,576.8C425.8,574.9 438.5,571.6 450.8,567C463.8,562.2 476.3,555.9 488.2,548.4L489.7,547.4C492.3,545.7 494.8,543.8 497.4,542C498.7,541 500.1,540.1 501.5,539.1C505.5,536 509.6,532.8 513.4,529.3C518.9,524.6 524,519.5 529,514.1C564.4,475.6 585.5,424.4 588.3,370.3C588.6,366.1 588.7,361.9 588.7,357.6C588.7,352.8 588.5,348 588.2,343.3C585.1,289.6 564.3,241.1 532.1,204.6L530.7,203C532.6,201.5 534.4,199.9 536.4,198.4C540.2,195.5 544,192.7 548,190C549.8,188.8 551.5,187.7 553.4,186.6C589.2,227.4 612.4,281 616.3,340.3C616.4,341.3 616.5,342.3 616.6,343.3C616.6,343.7 616.6,344.2 616.6,344.6C616.8,349.2 617,353.8 617,358.5Z" + android:fillColor="#0042B3"/> + <path + android:pathData="M506.3,112.5C497.5,118 488.9,124.1 480.7,130.7C477.3,133.4 473.9,136.2 470.6,139.1C462.3,141 454.1,143.5 446.1,146.5C445.1,146.9 443.9,147.3 442.8,147.7C440,148.8 437.1,150 434.3,151.3C433.9,151.5 433.6,151.6 433.2,151.8C432.2,152.3 431.2,152.8 430.2,153.3C428.1,154.3 425.9,155.4 423.8,156.5C422.6,157 421.5,157.7 420.3,158.3C418.4,159.4 416.4,160.5 414.6,161.6C413.4,162.3 412.3,163 411.1,163.8C410.4,164.2 409.7,164.5 409.1,165C408.4,165.4 407.7,165.9 407,166.4C406.9,166.4 406.8,166.6 406.6,166.6C404.5,168.1 402.4,169.5 400.3,171C399,171.9 397.6,172.9 396.3,173.9C394.2,175.4 392.2,177.1 390.1,178.7C388.9,179.7 387.7,180.7 386.5,181.8C384.3,183.6 382.2,185.5 380.1,187.4C379.1,188.4 378,189.3 377.1,190.2C374,193.2 371,196.2 368.1,199.3C356.6,211.8 346.6,225.6 338.3,240.5C329.8,255.6 323,271.8 318.1,288.6L317.6,290C316,295.5 314.5,301 313.2,306.6C312,312.2 310.9,317.8 310.1,323.5C309.6,326.9 309.3,330.4 309,333.9C308.8,336.1 308.4,338.3 308.3,340.6C308.2,341.5 308.1,342.4 308.1,343.3C307.7,348.1 307.6,353 307.6,357.8C307.6,359.7 307.6,361.7 307.7,363.6C307.7,365.6 307.8,367.5 307.9,369.5V370.3C308,372 308.1,373.7 308.3,375.3C308.4,377.2 308.6,379.2 308.8,381.1C308.9,383.1 309.2,385 309.4,386.9C309.7,388.9 309.9,390.8 310.2,392.7C310.5,394.6 310.8,396.6 311.1,398.4C311.5,400.3 311.9,402.3 312.2,404.2C312.6,406.1 313,408 313.5,409.9C313.9,411.8 314.3,413.6 314.8,415.5C315.3,417.4 315.8,419.3 316.3,421.1C316.9,423 317.4,424.8 317.9,426.7C318.5,428.6 319.1,430.4 319.7,432.2C320.3,434.1 321,435.8 321.7,437.7C322.3,439.5 323,441.3 323.7,443.1C324.4,444.9 325.2,446.7 325.9,448.5C326.7,450.2 327.4,452 328.2,453.7C329,455.5 329.8,457.2 330.7,459C331.5,460.7 332.3,462.4 333.3,464.1C334.1,465.8 335,467.5 335.9,469.2C336.9,470.8 337.8,472.5 338.8,474.2C339.7,475.8 340.7,477.4 341.7,479.1C342.7,480.7 343.7,482.3 344.8,483.9C345.8,485.5 346.9,487.1 348,488.6C349.1,490.2 350.2,491.7 351.3,493.3C352.4,494.8 353.5,496.3 354.7,497.8C355.8,499.3 357,500.8 358.2,502.3C359.5,503.7 360.7,505.2 361.9,506.6C363.1,508 364.4,509.4 365.6,510.8C366.9,512.2 368.2,513.6 369.5,514.9C370.7,516.3 372.1,517.6 373.4,519C374.8,520.3 376.1,521.6 377.5,522.9C378.8,524.2 380.2,525.4 381.6,526.6C383,527.9 384.5,529.2 385.9,530.3C387.3,531.5 388.8,532.7 390.2,533.9C391.7,535.1 393.1,536.2 394.6,537.3C396.1,538.4 397.6,539.5 399.1,540.6C400.6,541.7 402.2,542.7 403.7,543.7C404.8,544.5 405.9,545.2 407,545.9C407.4,546.2 407.9,546.5 408.4,546.8C409.4,547.5 410.4,548.1 411.5,548.7C410,548.9 408.5,549.1 407,549.2C404.4,549.6 401.9,550 399.3,550.2C394.6,550.6 390,550.8 385.3,550.8C383.7,550.8 382.1,550.8 380.5,550.7C378.9,550.7 377.3,550.6 375.7,550.5C374.2,550.4 372.6,550.3 371,550.2C369.4,550 367.9,549.9 366.3,549.7C364.7,549.5 363.1,549.3 361.6,549.1C361,549 360.5,548.9 359.9,548.8C355.4,544.6 351,540.2 346.7,535.6C322,509.2 303.4,477.5 291.9,442.9C291.2,440.7 290.5,438.5 289.8,436.2C289.7,435.8 289.6,435.4 289.5,435C288.8,432.7 288.2,430.4 287.6,428.2C286.9,425.7 286.3,423.3 285.8,420.9C285.3,418.7 284.7,416.5 284.3,414.3C283.8,411.8 283.3,409.2 282.8,406.6C282.4,404.5 282.1,402.3 281.8,400.2C281.4,398 281.1,395.9 280.8,393.7C280.6,391.5 280.3,389.3 280.1,387.1C279.9,384.9 279.7,382.7 279.5,380.5C279.4,378.3 279.2,376.1 279.1,374C279.1,372.8 279,371.5 279,370.3C279,369.3 278.9,368.3 278.9,367.3C278.8,365.1 278.8,362.9 278.8,360.7C278.8,354.9 279,349.1 279.4,343.3C279.4,342.6 279.5,341.9 279.5,341.3C280,334.7 280.7,328.3 281.6,321.9C282.6,315.5 283.8,309.1 285.3,302.7C286.6,296.4 288.3,290.2 290.2,284C292.1,277.8 294.2,271.7 296.6,265.7C298.8,259.7 301.4,253.8 304.2,248C304.6,247 305.1,246.1 305.6,245.2C306.4,243.6 307.1,242 307.9,240.5C308.3,239.9 308.7,239.2 309,238.5C310.3,236 311.7,233.5 313.1,231C316.3,225.4 319.7,220 323.3,214.7C326.9,209.5 330.7,204.4 334.6,199.5C338.6,194.5 342.7,189.7 347,185.1C351.4,180.6 355.8,176.1 360.5,171.9C363.5,169.1 366.7,166.6 369.9,163.9C371.6,162.6 373.2,161.1 374.8,159.8C379.8,156 384.9,152.4 390,149C395.1,145.7 400.3,142.6 405.6,139.7L406,139.5C406.4,139.3 406.6,139.1 407,139C407.7,138.6 408.4,138.2 409.2,137.8C409.6,137.6 410,137.4 410.5,137.1C414.5,135.1 418.5,133.1 422.6,131.3C426.5,129.6 430.4,128.1 434.3,126.6C436.1,125.9 437.9,125.2 439.8,124.6C444.8,122.9 449.8,121.3 454.9,119.9C455.7,119.7 456.6,119.5 457.4,119.2C463.3,117.8 469.3,116.5 475.3,115.5C481.4,114.4 487.4,113.7 493.5,113.2C497.8,112.8 502,112.6 506.3,112.5Z" + android:fillColor="#0042B3"/> + <path + android:pathData="M744.8,360.7C744.8,363.9 744.7,367.1 744.6,370.3C744.5,373.6 744.2,376.9 744,380.2C743.6,386.7 742.8,393.1 741.9,399.5C741,405.3 739.9,411 738.7,416.7C738.5,417.7 738.3,418.6 738.1,419.6C737.5,422.2 736.9,424.7 736.2,427.2C734.9,432.2 733.5,437.1 731.9,441.9C720.5,476.9 701.8,509 676.9,535.6C661.8,551.8 644.9,565.6 626.6,576.6H626.8C623.8,578.5 620.7,580.3 617.5,582C617.4,582 617.2,582.2 617,582.2C611.7,585.1 606.4,587.7 600.9,590.1C595.3,592.6 589.5,594.8 583.8,596.8C581.2,597.8 578.7,598.6 576.2,599.4C555.4,605.7 533.8,609 511.8,609H509C507.8,609 506.5,608.9 505.3,608.9H505.1C517.5,602 529.5,594 540.9,585.1C546.1,581 551.2,576.6 556.2,572.1L556.3,571.9C563.1,570.3 569.8,568.3 576.4,565.9L576.6,566C581.3,564.4 586,562.5 590.6,560.5C595.6,558.2 600.4,555.8 605.2,553.3C609.2,551 613.2,548.6 617.1,546.1C617.8,545.7 618.5,545.3 619.2,544.8C623.8,541.8 628.3,538.6 632.6,535.2C636.9,531.9 641.1,528.4 645.2,524.6C649.2,520.8 653.2,516.9 657,512.9C660.8,508.9 664.4,504.6 667.9,500.3C671.4,495.9 674.7,491.4 677.8,486.7C681,482.1 684,477.3 686.7,472.4C689.5,467.5 692.2,462.5 694.6,457.4C697.1,452.2 699.3,447 701.4,441.8C703.4,436.4 705.3,431 706.9,425.6C708.6,420.1 710,414.6 711.3,409C712.5,403.4 713.6,397.8 714.4,392.1C715.2,386.5 716,370.3 716,370.3C716.2,366.5 716.3,362.7 716.3,358.9C716.3,354.4 716.2,350 715.9,345.7C715.9,344.9 715.7,344.1 715.7,343.3C714.8,325.5 712,308.3 707.5,291.9C707.3,291.3 707.1,290.7 706.9,290C706.9,290 706.9,289.9 706.9,289.9C692.1,238.3 660.4,194.8 618.8,167.1C618.2,166.7 617.6,166.3 617.1,166C616.8,165.8 616.6,165.7 616.4,165.5C615.5,165 614.6,164.4 613.7,163.8C614.5,163.6 615.5,163.6 616.4,163.4C616.6,163.4 616.8,163.4 617.1,163.3C620.5,162.8 624,162.3 627.4,162C632,161.6 636.7,161.4 641.4,161.4C644.7,161.4 647.9,161.5 651.2,161.7C653.1,163.3 655.1,164.9 657,166.5C659.1,168.3 661.1,170.1 663.1,171.9C667.8,176.1 672.2,180.6 676.6,185.1C680.9,189.7 685,194.5 689,199.5C692.9,204.4 696.7,209.5 700.3,214.7C703.9,220 707.3,225.4 710.5,231C713.7,236.5 716.7,242.2 719.4,248C722.2,253.8 724.8,259.7 727,265.7C729.4,271.7 731.5,277.8 733.4,284C735.3,290.2 737,296.4 738.3,302.7C739.8,309.1 741,315.5 742,321.9C742.9,328.3 743.6,334.7 744.1,341.3C744.1,341.9 744.2,342.6 744.2,343.3C744.6,349.1 744.8,354.9 744.8,360.7H744.8Z" + android:fillColor="#0042B3"/> + <path + android:pathData="M873.8,351.2C873.7,348.8 873.7,346.4 873.5,344C873.5,344.4 873.6,344.8 873.6,345.1C873.6,344.5 873.5,343.8 873.5,343.2C868.4,251.8 817.6,173.6 745.2,134.6C745.2,134.6 745.1,134.5 745,134.4C723,122.6 698.9,114.3 673.5,110.4C668.7,109.7 663.7,109.1 658.7,108.7C658.2,108.7 657.6,108.7 657.1,108.6C656.1,108.6 655.1,108.5 654.2,108.5C651,108.3 647.9,108.2 644.7,108.1C643.6,108.1 642.4,108.1 641.3,108.1C639,108.1 636.9,108.1 634.6,108.2C633.2,108.2 631.9,108.3 630.5,108.3C628.8,108.4 627.1,108.5 625.4,108.7C624.6,108.7 623.8,108.7 623,108.9C621,109 619,109.3 617.1,109.5C616.8,109.5 616.6,109.5 616.4,109.5C612.5,110 608.6,110.5 604.8,111.2C598.8,112.2 592.8,113.4 586.9,114.9C583.5,115.8 580.1,116.8 576.7,117.8C574.3,118.6 571.9,119.4 569.5,120.2C569.5,120.2 569.3,120.2 569.3,120.2C563.5,122.2 557.7,124.5 552.1,127C546.5,129.5 540.9,132.2 535.5,135.1C535.1,135.4 534.7,135.6 534.3,135.8C532.4,136.9 530.4,137.9 528.5,139C528.5,139 528.4,139 528.4,139.1C525.4,140.9 522.5,142.7 519.5,144.6C517.1,146.1 514.7,147.8 512.4,149.5C509.6,151.4 507,153.4 504.3,155.4C501.5,157.6 498.9,159.9 496.3,162.2C494.1,164 492,165.6 490,167.5C489.9,167.6 489.8,167.6 489.7,167.7C489.3,168.2 488.8,168.6 488.3,169C484.2,172.8 480.3,176.7 476.5,180.7C475.1,182.2 473.8,183.8 472.5,185.3C472,185.8 471.5,186.3 471.1,186.9C470.2,188 469.3,188.9 468.5,190C467,191.7 465.5,193.3 464.1,195C463.6,195.6 463.2,196.3 462.7,196.9C459.9,200.5 457,204.1 454.4,207.9C453,210 451.7,212.2 450.3,214.2C447.2,218.8 444.4,223.5 441.6,228.3C439.4,232.2 437.2,236.2 435.2,240.3C434.9,240.9 434.6,241.5 434.3,242C434.1,242.5 433.9,243 433.6,243.5C430.9,249.3 428.3,255.2 426,261.2C423.6,267.2 421.5,273.3 419.7,279.5C417.8,285.7 416.1,291.9 414.7,298.2C413.2,304.6 412,310.9 411.1,317.3C410.1,323.8 409.1,330.1 408.6,336.6C408.4,338.7 407.6,350.5 407.5,352.6C407.3,357 407.3,370.2 407.3,370.2C409.9,428.9 431.7,484.9 468.9,528.1C470.1,527.4 471.3,526.6 472.6,525.8C474.6,524.6 476.7,523.4 478.7,522.1C482.7,519.5 486.5,516.7 490.3,513.7C490.9,513.2 491.6,512.6 492.2,512.1C492.9,511.5 493.6,511 494.2,510.5C493,509.1 491.8,507.8 490.6,506.4C489.4,505 488.2,503.6 487,502.1C485.8,500.6 484.6,499.2 483.4,497.7C482.3,496.2 481.1,494.6 480,493.1C478.8,491.6 477.7,490 476.7,488.5C475.6,486.9 474.5,485.3 473.4,483.7C472.4,482.1 471.4,480.5 470.3,478.9C469.3,477.3 468.3,475.6 467.4,474C466.4,472.3 465.5,470.7 464.6,469C463.7,467.3 462.8,465.6 461.9,463.9C461.1,462.2 460.2,460.5 459.3,458.8C458.5,457.1 457.7,455.3 456.9,453.6C456.1,451.8 455.3,450.1 454.6,448.3C453.8,446.5 453.1,444.7 452.4,443C451.7,441.2 451,439.3 450.3,437.6C449.6,435.7 449,434 448.4,432.1C447.8,430.3 447.2,428.4 446.7,426.6C446.1,424.8 445.5,422.9 445,421C444.5,419.2 444,417.3 443.5,415.4C443.1,413.5 442.6,411.6 442.1,409.7C441.7,407.9 441.3,406 440.9,404.1C440.5,402.2 440.2,400.3 439.8,398.3C439.5,396.4 439.2,394.5 438.9,392.6C438.6,390.6 438.4,388.8 438.1,386.8C437.9,384.9 437.7,383 437.5,381C437.3,379.1 437.1,377.1 437,375.2C436.9,374.2 436.9,373.1 436.7,372.1C436.7,371.5 436.7,370.9 436.6,370.3V369.4C436.5,367.5 436.5,365.5 436.4,363.6C436.4,361.6 436.3,359.7 436.3,357.7C436.3,352.9 436.5,348 436.8,343.2C436.8,342.8 436.9,342.2 436.9,341.8C436.9,341.4 436.9,341 437,340.5C437.4,334.8 438,329.1 438.9,323.4C439.7,317.7 440.8,312.1 442,306.5C443.3,300.9 444.7,295.4 446.4,289.9C448,284.5 449.9,279.1 451.9,273.8C454,268.5 456.2,263.3 458.6,258.1C461.1,253 463.7,247.9 466.5,243.1C467,242.2 467.5,241.2 468.1,240.4C470.4,236.4 472.8,232.6 475.4,228.7C478.5,224.1 481.9,219.6 485.4,215.2C488.9,210.8 492.5,206.6 496.3,202.5C500,198.4 504,194.5 508.1,190.8C509.5,189.5 511,188.2 512.5,186.9C515.2,184.6 517.9,182.3 520.7,180.2C525,176.8 529.5,173.6 534,170.6C534.8,170.1 535.7,169.5 536.5,169C539,167.4 541.6,165.8 544.3,164.3C545.5,163.6 546.8,162.9 548,162.2C552.8,159.6 557.7,157.2 562.7,155C567.2,153 571.9,151.1 576.6,149.4L577.7,149C579.1,148.6 580.5,148 581.9,147.6C580.8,147.1 580.8,147.1 581.9,147.6C585.7,146.4 589.4,145.3 593.2,144.3C598.4,143 603.7,141.8 608.9,140.9C611.4,140.5 613.9,140.2 616.3,139.8C616.6,139.8 616.8,139.8 617,139.7C617.4,139.7 617.8,139.6 618.2,139.5C620.4,139.3 622.5,139.1 624.7,138.9H624.9C630.3,138.4 635.6,138.2 641,138.2C646.3,138.2 651.7,138.4 657,138.9C662.4,139.3 667.7,140 673,140.9C675.4,141.3 677.8,141.7 680.2,142.2C683.1,142.8 685.9,143.5 688.7,144.2C694,145.6 699.1,147.2 704.2,148.9C709.3,150.7 714.4,152.7 719.3,154.9C724.2,157 729.1,159.4 733.9,162.1C737.7,164.2 741.4,166.5 745,168.8C745.1,168.8 745.2,168.9 745.2,168.9C746.1,169.5 747,169.9 747.9,170.5C752.4,173.4 756.9,176.7 761.2,180C765.6,183.4 769.8,187 773.8,190.7C777.9,194.4 781.9,198.3 785.6,202.4C789.4,206.5 793.1,210.7 796.5,215.1C800,219.4 803.4,224 806.5,228.6C809.6,233.3 812.6,238 815.4,243C818.2,247.8 820.8,252.9 823.3,258C825.7,263.1 828,268.3 830,273.6C831.9,278.7 833.6,283.8 835.2,288.9C835.5,289.8 835.8,290.6 836,291.5C840.6,307.9 843.4,325.2 844.4,343C844.4,343.7 844.5,344.4 844.5,345C844.8,349.5 844.9,354.1 844.9,358.6C844.9,362.5 844.8,366.3 844.6,370.1C844.6,370.1 843.9,386.2 843.1,391.9C842.2,397.6 841.2,403.2 839.9,408.8C838.7,414.4 837.2,420 835.6,425.4C833.9,430.9 832.1,436.2 830,441.6C828,446.9 825.7,452.1 823.3,457.2C820.9,462.3 818.3,467.4 815.5,472.3C812.6,477.1 809.7,482 806.5,486.6C803.4,491.2 800,495.8 796.6,500.1C793.1,504.5 789.5,508.7 785.7,512.8C781.9,516.8 778,520.7 773.9,524.5C769.8,528.2 765.6,531.7 761.3,535.1C756.9,538.5 752.5,541.7 747.9,544.7C747.1,545.3 746.1,545.7 745.3,546.3C741.6,548.7 737.8,551 733.9,553.1C729.1,555.7 724.2,558.1 719.3,560.3C714.4,562.5 709.3,564.5 704.2,566.3C699.9,567.8 695.4,569.1 690.9,570.3C675.6,585.3 658.9,598.2 641,608.8C677.6,608.8 713.2,599.4 745.2,582.1C767.3,570.2 787.8,554.6 805.5,535.4C846.7,491.2 870.9,432.2 873.7,370.1C873.7,369.3 873.7,368.3 873.7,367.5C873.8,364.4 873.9,361.3 873.9,358.3C873.9,355.9 873.8,353.5 873.7,351.1L873.8,351.2Z" + android:fillColor="#0042B3"/> + </group> +</vector> diff --git a/merchant-terminal/src/main/res/drawable/ic_taler_pos_logo_monochrome.xml b/merchant-terminal/src/main/res/drawable/ic_taler_pos_logo_monochrome.xml @@ -0,0 +1,41 @@ +<!-- + ~ Copyright (C) 2026 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + <group android:scaleX="0.6" + android:scaleY="0.6" + android:translateX="204.8" + android:translateY="204.8"> + <path + android:pathData="M221.1,890.8V669.2H313C322.5,669.2 331.3,671.2 339.2,675.1C347.3,679.1 354.3,684.5 360.1,691.3C365.9,698 370.5,705.5 373.9,713.8C377.2,721.9 378.9,730.2 378.9,738.8C378.9,750.8 376.1,762.3 370.7,773.1C365.5,783.7 358.1,792.3 348.6,799C339,805.7 327.7,809 314.8,809H243V890.8H221.1ZM243,789.6H313.9C322.6,789.6 330.2,787.3 336.7,782.5C343.1,777.7 348.1,771.4 351.7,763.7C355.2,756 357,747.7 357,738.8C357,729.6 354.9,721.2 350.7,713.5C346.6,705.8 341.1,699.8 334.2,695.4C327.5,690.8 320.1,688.5 312,688.5H243V789.6ZM504.7,892.3C489.1,892.3 475,889.2 462.3,883C449.6,876.7 438.6,868.3 429.5,857.7C420.3,846.9 413.2,834.8 408.2,821.5C403.2,808 400.7,794.1 400.7,780C400.7,765.2 403.3,751.2 408.5,737.8C413.8,724.3 421,712.4 430.4,702C440,691.3 451.1,683 463.8,677C476.5,670.7 490.3,667.6 505.1,667.6C520.7,667.6 534.8,670.8 547.5,677.3C560.2,683.7 571.1,692.4 580,703.2C589.2,714 596.3,726.1 601.3,739.4C606.2,752.7 608.7,766.3 608.7,780.3C608.7,795.1 606.1,809.2 600.9,822.7C595.7,836 588.4,848 579.1,858.6C569.7,869 558.7,877.2 546,883.3C533.3,889.3 519.5,892.3 504.7,892.3ZM422.6,780C422.6,792 424.6,803.7 428.5,814.9C432.5,825.9 438.1,835.8 445.4,844.6C452.7,853.1 461.3,860 471.3,865.2C481.5,870.2 492.7,872.7 504.7,872.7C517.4,872.7 528.8,870.1 538.8,864.9C549,859.4 557.6,852.4 564.7,843.6C572,834.7 577.5,824.7 581.3,813.7C585,802.6 586.9,791.4 586.9,780C586.9,767.9 584.9,756.4 581,745.3C577,734.3 571.3,724.4 563.8,715.7C556.5,706.9 547.8,700.1 537.8,695.1C527.9,690.1 516.8,687.6 504.7,687.6C492.3,687.6 480.9,690.2 470.7,695.4C460.7,700.6 452.1,707.7 444.8,716.6C437.7,725.4 432.2,735.2 428.2,746.3C424.5,757.3 422.6,768.5 422.6,780ZM779.6,710.7C776.5,707.2 772.8,704 768.6,701.3C764.5,698.4 759.8,695.9 754.6,693.8C749.4,691.8 743.8,690.1 737.7,688.8C731.9,687.6 725.7,687 719,687C699.6,687 685.4,690.7 676.2,698.2C667.2,705.5 662.8,715.5 662.8,728.2C662.8,736.9 664.9,743.8 669,748.8C673.4,753.8 680.2,757.8 689.3,760.9C698.5,764.1 710.1,767.3 724.3,770.6C740.1,773.9 753.8,777.9 765.2,782.5C776.7,787 785.5,793.3 791.8,801.2C798,808.9 801.1,819.4 801.1,832.7C801.1,842.9 799.2,851.7 795.2,859.2C791.2,866.7 785.7,873 778.6,878C771.6,883 763.1,886.7 753.3,889.2C743.6,891.5 732.8,892.6 721.2,892.6C709.7,892.6 698.7,891.5 688.1,889.2C677.7,886.7 667.8,883.2 658.4,878.6C649,873.8 640.3,867.8 632.2,860.5L643.1,842.7C647,846.9 651.7,850.8 657.2,854.6C662.8,858.1 668.9,861.3 675.6,864.2C682.4,867.1 689.7,869.4 697.4,871.1C705.4,872.5 713.5,873.3 721.8,873.3C739.5,873.3 753.2,870.1 763,863.6C773,857.2 778,847.6 778,834.9C778,825.7 775.5,818.5 770.5,813C765.5,807.4 758,802.9 748,799.3C738,795.8 725.8,792.3 711.2,789C695.8,785.5 682.8,781.5 672.1,777.2C661.5,772.8 653.5,767.1 648.1,760C642.9,752.7 640.3,743.1 640.3,731.3C640.3,717.6 643.6,706 650.3,696.6C657.2,687.1 666.5,679.9 678.4,675.1C690.3,670.1 703.9,667.6 719.3,667.6C729.1,667.6 738.1,668.7 746.5,670.7C755,672.6 762.8,675.4 769.9,679.2C777.2,682.9 784,687.6 790.2,693.2L779.6,710.7Z" + android:fillColor="#000000"/> + <path + android:pathData="M617,358.5C617,373.9 614.2,388.8 613.3,395.2C612.3,401.7 611.1,408 609.7,414.3C608.3,420.6 606.6,426.9 604.7,433.1C602.9,439.3 600.7,445.4 598.4,451.4C596.1,457.4 593.5,463.3 590.8,469.1C588,474.9 585,480.6 581.8,486.1C578.7,491.7 575.2,497 571.7,502.3C568,507.6 564.3,512.7 560.3,517.6C556.4,522.6 552.2,527.4 547.9,532C543.6,536.6 539.1,540.9 534.5,545.2H534.4C533.4,546.1 532.3,547 531.2,547.9C529.9,549.1 528.5,550.2 527.2,551.4C524.9,553.4 522.5,555.4 520.1,557.3C516.4,560.1 512.7,562.8 508.9,565.4C507.6,566.3 506.2,567.2 504.9,568.1C500.6,570.9 496.2,573.6 491.8,576.1C490.8,576.6 489.9,577.1 488.9,577.6C483.5,580.6 478,583.3 472.4,585.8C466.7,588.3 460.9,590.5 455.1,592.5C450.3,594.2 445.4,595.7 440.5,597.1C439.5,597.3 438.5,597.6 437.5,597.8C431.6,599.4 425.6,600.6 419.6,601.6C415.4,602.3 411.2,602.9 407,603.3C405.2,603.6 403.3,603.8 401.4,603.9C395.5,604.4 389.4,604.7 383.4,604.7H383.2C381.1,604.7 379.1,604.6 376.9,604.6C374.9,604.5 372.8,604.5 370.7,604.4C368.7,604.2 366.6,604.1 364.6,603.9C362.5,603.7 360.5,603.5 358.4,603.3C356.4,603 354.3,602.7 352.2,602.5C350.2,602.2 348.2,601.9 346.1,601.5C344.1,601.2 342,600.8 340,600.4C338,600 335.9,599.5 333.9,599.1C331.9,598.6 329.9,598.1 327.9,597.6C325.9,597.1 323.8,596.5 321.9,595.9C319.9,595.3 317.9,594.7 315.9,594.1C313.9,593.5 312,592.8 310,592.1C308,591.4 306.1,590.7 304.1,590C302.2,589.2 300.3,588.4 298.3,587.6C296.4,586.8 294.5,586 292.6,585.1C290.7,584.3 288.8,583.4 286.9,582.5C285,581.6 283.1,580.7 281.3,579.7C280.3,579.2 279.3,578.6 278.3,578.1C277.5,577.6 276.6,577.2 275.7,576.7C273.9,575.7 272.1,574.6 270.3,573.6C268.5,572.5 266.7,571.4 264.9,570.3C263.1,569.2 261.3,568 259.6,566.8C257.9,565.7 256.1,564.5 254.4,563.3C252.6,562.1 251,560.8 249.3,559.6C247.6,558.3 245.9,557 244.2,555.7C242.6,554.4 240.9,553 239.3,551.7C237.7,550.3 236.1,548.9 234.5,547.5C232.9,546.1 231.3,544.7 229.8,543.2C228.2,541.8 226.7,540.3 225.2,538.9C223.6,537.3 222.1,535.8 220.6,534.3C219.1,532.7 217.7,531.2 216.3,529.6C214.8,528 213.4,526.4 212,524.8C210.6,523.2 209.2,521.5 207.9,519.9C206.5,518.2 205.2,516.5 203.8,514.9C202.5,513.2 201.2,511.4 199.9,509.7C198.6,508 197.4,506.2 196.2,504.4C195,502.7 193.7,500.9 192.5,499.1C191.3,497.3 190.2,495.5 189,493.7C187.9,491.8 186.8,489.9 185.7,488.1C184.6,486.2 183.5,484.3 182.5,482.4C181.4,480.5 180.4,478.6 179.4,476.7C178.4,474.8 177.4,472.8 176.4,470.9C175.5,468.9 174.5,466.9 173.7,465C172.8,463 171.8,461 171,459C170.2,457 169.3,454.9 168.5,452.9C167.7,450.9 167,448.9 166.2,446.8C165.4,444.7 164.7,442.7 164,440.6C163.3,438.5 162.6,436.5 162,434.4C161.4,432.3 160.7,430.2 160.1,428C159.5,425.9 158.9,423.9 158.4,421.7C157.9,419.6 157.3,417.4 156.8,415.3C156.4,413.2 155.9,411 155.4,408.9C155,406.7 154.6,404.5 154.2,402.4C153.8,400.2 153.5,398 153.2,395.9C152.8,393.7 152.5,391.5 152.2,389.3C152,387.1 151.7,385 151.5,382.8C151.3,380.6 151.1,378.4 150.9,376.2C150.8,374.2 150.2,367 150.2,365.4C150.1,363.1 150,360.8 150,358.5C150,353.4 150.2,348.3 150.5,343.3C152.5,307.7 161.6,273.2 176.8,242C177.1,241.5 177.3,241 177.5,240.5C180.1,235.3 182.9,230.3 185.7,225.3C188.5,220.6 191.5,215.9 194.6,211.4H194.7C197.5,207.1 200.6,202.9 203.8,198.9C206.3,195.6 208.9,192.4 211.5,189.3C211.9,188.9 212.3,188.5 212.7,188C214.6,185.8 216.4,183.5 218.4,181.4C236.1,162.4 256.4,146.8 278.3,135C287.2,130.1 296.2,125.9 305.6,122.4C306.7,121.9 307.9,121.6 309,121.2C327.6,114.4 347.1,110.2 367,108.7C372.5,108.3 378,108 383.5,108C386.3,108 389,108.1 391.8,108.1L390.8,108.7L390.6,108.8C374.3,117.7 358.8,128.3 344.4,140.7C332.3,143.4 320.5,147.3 309,152.3C307.9,152.9 306.7,153.3 305.6,153.8C296.2,158.2 287,163.2 278.3,169.1C264.8,178.1 252.1,188.8 240.8,201.1L240,201.9C237.7,204.4 235.5,207 233.3,209.6C230.8,212.5 228.4,215.5 226,218.5C224.4,220.7 222.8,222.9 221.2,225.1C221,225.4 220.8,225.6 220.6,225.9C219.8,227.1 218.9,228.2 218.1,229.4C215,233.9 212.2,238.5 209.5,243.2C206.7,248.1 204.1,253.1 201.7,258.2C199.2,263.3 197,268.5 195,273.9C192.9,279.1 191,284.5 189.4,290C187.7,295.5 186.2,301 185,306.6C183.8,312.2 182.7,317.8 181.9,323.5C181,329.2 180.4,334.8 180,340.6C179.9,341.5 179.9,342.4 179.8,343.3C179.5,348.1 179.4,353 179.4,357.8C179.4,359.7 179.4,361.7 179.4,363.6C179.4,365.6 179.5,367.5 179.6,369.5V370.3C179.8,372 179.9,373.7 180,375.3C180.2,377.2 180.3,379.2 180.5,381.1C180.7,383.1 180.9,385 181.1,386.9C181.4,388.9 181.6,390.8 181.9,392.7C182.2,394.6 182.6,396.6 182.9,398.4C183.3,400.3 183.6,402.3 183.9,404.2C184.3,406.1 184.7,408 185.2,409.9C185.6,411.8 186.1,413.6 186.6,415.5C187,417.4 187.6,419.3 188.1,421.1C188.6,423 189.1,424.8 189.7,426.7C190.3,428.6 190.9,430.4 191.5,432.2C192.1,434.1 192.7,435.8 193.4,437.7C194,439.5 194.7,441.3 195.5,443.1C196.2,444.9 196.9,446.7 197.6,448.5C198.4,450.2 199.1,452 199.9,453.7C200.7,455.5 201.5,457.2 202.4,459C203.2,460.7 204.1,462.4 205,464.1C205.8,465.8 206.8,467.5 207.7,469.2C208.6,470.8 209.5,472.5 210.5,474.2C211.5,475.8 212.4,477.4 213.5,479.1C214.4,480.7 215.5,482.3 216.5,483.9C217.5,485.5 218.6,487.1 219.7,488.6C220.8,490.2 221.9,491.7 223,493.3C224.1,494.8 225.3,496.3 226.4,497.8C227.6,499.3 228.8,500.8 230,502.3C231.2,503.7 232.4,505.2 233.6,506.6C234.9,508 236.1,509.4 237.4,510.8C238.6,512.2 239.9,513.6 241.2,514.9C242.5,516.3 243.9,517.6 245.2,519C246.5,520.3 247.9,521.6 249.3,522.9C250,523.6 250.8,524.3 251.6,525C252.2,525.6 252.7,526.1 253.4,526.6C253.4,526.7 253.4,526.7 253.4,526.8C254.8,528 256.2,529.2 257.6,530.3C258.4,531 259.3,531.7 260.1,532.4C260.7,532.9 261.3,533.4 261.9,533.9C262.2,534.1 262.5,534.3 262.8,534.5C267.8,538.6 272.9,542.6 278.3,546.2C310.1,567.4 346.9,578.9 384.9,578.9C389.8,578.9 394.8,578.7 399.8,578.4C402.2,578.1 404.6,577.8 407,577.5C408.9,577.3 410.9,577.1 412.8,576.8C425.8,574.9 438.5,571.6 450.8,567C463.8,562.2 476.3,555.9 488.2,548.4L489.7,547.4C492.3,545.7 494.8,543.8 497.4,542C498.7,541 500.1,540.1 501.5,539.1C505.5,536 509.6,532.8 513.4,529.3C518.9,524.6 524,519.5 529,514.1C564.4,475.6 585.5,424.4 588.3,370.3C588.6,366.1 588.7,361.9 588.7,357.6C588.7,352.8 588.5,348 588.2,343.3C585.1,289.6 564.3,241.1 532.1,204.6L530.7,203C532.6,201.5 534.4,199.9 536.4,198.4C540.2,195.5 544,192.7 548,190C549.8,188.8 551.5,187.7 553.4,186.6C589.2,227.4 612.4,281 616.3,340.3C616.4,341.3 616.5,342.3 616.6,343.3C616.6,343.7 616.6,344.2 616.6,344.6C616.8,349.2 617,353.8 617,358.5Z" + android:fillColor="#000000"/> + <path + android:pathData="M506.3,112.5C497.5,118 488.9,124.1 480.7,130.7C477.3,133.4 473.9,136.2 470.6,139.1C462.3,141 454.1,143.5 446.1,146.5C445.1,146.9 443.9,147.3 442.8,147.7C440,148.8 437.1,150 434.3,151.3C433.9,151.5 433.6,151.6 433.2,151.8C432.2,152.3 431.2,152.8 430.2,153.3C428.1,154.3 425.9,155.4 423.8,156.5C422.6,157 421.5,157.7 420.3,158.3C418.4,159.4 416.4,160.5 414.6,161.6C413.4,162.3 412.3,163 411.1,163.8C410.4,164.2 409.7,164.5 409.1,165C408.4,165.4 407.7,165.9 407,166.4C406.9,166.4 406.8,166.6 406.6,166.6C404.5,168.1 402.4,169.5 400.3,171C399,171.9 397.6,172.9 396.3,173.9C394.2,175.4 392.2,177.1 390.1,178.7C388.9,179.7 387.7,180.7 386.5,181.8C384.3,183.6 382.2,185.5 380.1,187.4C379.1,188.4 378,189.3 377.1,190.2C374,193.2 371,196.2 368.1,199.3C356.6,211.8 346.6,225.6 338.3,240.5C329.8,255.6 323,271.8 318.1,288.6L317.6,290C316,295.5 314.5,301 313.2,306.6C312,312.2 310.9,317.8 310.1,323.5C309.6,326.9 309.3,330.4 309,333.9C308.8,336.1 308.4,338.3 308.3,340.6C308.2,341.5 308.1,342.4 308.1,343.3C307.7,348.1 307.6,353 307.6,357.8C307.6,359.7 307.6,361.7 307.7,363.6C307.7,365.6 307.8,367.5 307.9,369.5V370.3C308,372 308.1,373.7 308.3,375.3C308.4,377.2 308.6,379.2 308.8,381.1C308.9,383.1 309.2,385 309.4,386.9C309.7,388.9 309.9,390.8 310.2,392.7C310.5,394.6 310.8,396.6 311.1,398.4C311.5,400.3 311.9,402.3 312.2,404.2C312.6,406.1 313,408 313.5,409.9C313.9,411.8 314.3,413.6 314.8,415.5C315.3,417.4 315.8,419.3 316.3,421.1C316.9,423 317.4,424.8 317.9,426.7C318.5,428.6 319.1,430.4 319.7,432.2C320.3,434.1 321,435.8 321.7,437.7C322.3,439.5 323,441.3 323.7,443.1C324.4,444.9 325.2,446.7 325.9,448.5C326.7,450.2 327.4,452 328.2,453.7C329,455.5 329.8,457.2 330.7,459C331.5,460.7 332.3,462.4 333.3,464.1C334.1,465.8 335,467.5 335.9,469.2C336.9,470.8 337.8,472.5 338.8,474.2C339.7,475.8 340.7,477.4 341.7,479.1C342.7,480.7 343.7,482.3 344.8,483.9C345.8,485.5 346.9,487.1 348,488.6C349.1,490.2 350.2,491.7 351.3,493.3C352.4,494.8 353.5,496.3 354.7,497.8C355.8,499.3 357,500.8 358.2,502.3C359.5,503.7 360.7,505.2 361.9,506.6C363.1,508 364.4,509.4 365.6,510.8C366.9,512.2 368.2,513.6 369.5,514.9C370.7,516.3 372.1,517.6 373.4,519C374.8,520.3 376.1,521.6 377.5,522.9C378.8,524.2 380.2,525.4 381.6,526.6C383,527.9 384.5,529.2 385.9,530.3C387.3,531.5 388.8,532.7 390.2,533.9C391.7,535.1 393.1,536.2 394.6,537.3C396.1,538.4 397.6,539.5 399.1,540.6C400.6,541.7 402.2,542.7 403.7,543.7C404.8,544.5 405.9,545.2 407,545.9C407.4,546.2 407.9,546.5 408.4,546.8C409.4,547.5 410.4,548.1 411.5,548.7C410,548.9 408.5,549.1 407,549.2C404.4,549.6 401.9,550 399.3,550.2C394.6,550.6 390,550.8 385.3,550.8C383.7,550.8 382.1,550.8 380.5,550.7C378.9,550.7 377.3,550.6 375.7,550.5C374.2,550.4 372.6,550.3 371,550.2C369.4,550 367.9,549.9 366.3,549.7C364.7,549.5 363.1,549.3 361.6,549.1C361,549 360.5,548.9 359.9,548.8C355.4,544.6 351,540.2 346.7,535.6C322,509.2 303.4,477.5 291.9,442.9C291.2,440.7 290.5,438.5 289.8,436.2C289.7,435.8 289.6,435.4 289.5,435C288.8,432.7 288.2,430.4 287.6,428.2C286.9,425.7 286.3,423.3 285.8,420.9C285.3,418.7 284.7,416.5 284.3,414.3C283.8,411.8 283.3,409.2 282.8,406.6C282.4,404.5 282.1,402.3 281.8,400.2C281.4,398 281.1,395.9 280.8,393.7C280.6,391.5 280.3,389.3 280.1,387.1C279.9,384.9 279.7,382.7 279.5,380.5C279.4,378.3 279.2,376.1 279.1,374C279.1,372.8 279,371.5 279,370.3C279,369.3 278.9,368.3 278.9,367.3C278.8,365.1 278.8,362.9 278.8,360.7C278.8,354.9 279,349.1 279.4,343.3C279.4,342.6 279.5,341.9 279.5,341.3C280,334.7 280.7,328.3 281.6,321.9C282.6,315.5 283.8,309.1 285.3,302.7C286.6,296.4 288.3,290.2 290.2,284C292.1,277.8 294.2,271.7 296.6,265.7C298.8,259.7 301.4,253.8 304.2,248C304.6,247 305.1,246.1 305.6,245.2C306.4,243.6 307.1,242 307.9,240.5C308.3,239.9 308.7,239.2 309,238.5C310.3,236 311.7,233.5 313.1,231C316.3,225.4 319.7,220 323.3,214.7C326.9,209.5 330.7,204.4 334.6,199.5C338.6,194.5 342.7,189.7 347,185.1C351.4,180.6 355.8,176.1 360.5,171.9C363.5,169.1 366.7,166.6 369.9,163.9C371.6,162.6 373.2,161.1 374.8,159.8C379.8,156 384.9,152.4 390,149C395.1,145.7 400.3,142.6 405.6,139.7L406,139.5C406.4,139.3 406.6,139.1 407,139C407.7,138.6 408.4,138.2 409.2,137.8C409.6,137.6 410,137.4 410.5,137.1C414.5,135.1 418.5,133.1 422.6,131.3C426.5,129.6 430.4,128.1 434.3,126.6C436.1,125.9 437.9,125.2 439.8,124.6C444.8,122.9 449.8,121.3 454.9,119.9C455.7,119.7 456.6,119.5 457.4,119.2C463.3,117.8 469.3,116.5 475.3,115.5C481.4,114.4 487.4,113.7 493.5,113.2C497.8,112.8 502,112.6 506.3,112.5Z" + android:fillColor="#000000"/> + <path + android:pathData="M744.8,360.7C744.8,363.9 744.7,367.1 744.6,370.3C744.5,373.6 744.2,376.9 744,380.2C743.6,386.7 742.8,393.1 741.9,399.5C741,405.3 739.9,411 738.7,416.7C738.5,417.7 738.3,418.6 738.1,419.6C737.5,422.2 736.9,424.7 736.2,427.2C734.9,432.2 733.5,437.1 731.9,441.9C720.5,476.9 701.8,509 676.9,535.6C661.8,551.8 644.9,565.6 626.6,576.6H626.8C623.8,578.5 620.7,580.3 617.5,582C617.4,582 617.2,582.2 617,582.2C611.7,585.1 606.4,587.7 600.9,590.1C595.3,592.6 589.5,594.8 583.8,596.8C581.2,597.8 578.7,598.6 576.2,599.4C555.4,605.7 533.8,609 511.8,609H509C507.8,609 506.5,608.9 505.3,608.9H505.1C517.5,602 529.5,594 540.9,585.1C546.1,581 551.2,576.6 556.2,572.1L556.3,571.9C563.1,570.3 569.8,568.3 576.4,565.9L576.6,566C581.3,564.4 586,562.5 590.6,560.5C595.6,558.2 600.4,555.8 605.2,553.3C609.2,551 613.2,548.6 617.1,546.1C617.8,545.7 618.5,545.3 619.2,544.8C623.8,541.8 628.3,538.6 632.6,535.2C636.9,531.9 641.1,528.4 645.2,524.6C649.2,520.8 653.2,516.9 657,512.9C660.8,508.9 664.4,504.6 667.9,500.3C671.4,495.9 674.7,491.4 677.8,486.7C681,482.1 684,477.3 686.7,472.4C689.5,467.5 692.2,462.5 694.6,457.4C697.1,452.2 699.3,447 701.4,441.8C703.4,436.4 705.3,431 706.9,425.6C708.6,420.1 710,414.6 711.3,409C712.5,403.4 713.6,397.8 714.4,392.1C715.2,386.5 716,370.3 716,370.3C716.2,366.5 716.3,362.7 716.3,358.9C716.3,354.4 716.2,350 715.9,345.7C715.9,344.9 715.7,344.1 715.7,343.3C714.8,325.5 712,308.3 707.5,291.9C707.3,291.3 707.1,290.7 706.9,290C706.9,290 706.9,289.9 706.9,289.9C692.1,238.3 660.4,194.8 618.8,167.1C618.2,166.7 617.6,166.3 617.1,166C616.8,165.8 616.6,165.7 616.4,165.5C615.5,165 614.6,164.4 613.7,163.8C614.5,163.6 615.5,163.6 616.4,163.4C616.6,163.4 616.8,163.4 617.1,163.3C620.5,162.8 624,162.3 627.4,162C632,161.6 636.7,161.4 641.4,161.4C644.7,161.4 647.9,161.5 651.2,161.7C653.1,163.3 655.1,164.9 657,166.5C659.1,168.3 661.1,170.1 663.1,171.9C667.8,176.1 672.2,180.6 676.6,185.1C680.9,189.7 685,194.5 689,199.5C692.9,204.4 696.7,209.5 700.3,214.7C703.9,220 707.3,225.4 710.5,231C713.7,236.5 716.7,242.2 719.4,248C722.2,253.8 724.8,259.7 727,265.7C729.4,271.7 731.5,277.8 733.4,284C735.3,290.2 737,296.4 738.3,302.7C739.8,309.1 741,315.5 742,321.9C742.9,328.3 743.6,334.7 744.1,341.3C744.1,341.9 744.2,342.6 744.2,343.3C744.6,349.1 744.8,354.9 744.8,360.7H744.8Z" + android:fillColor="#000000"/> + <path + android:pathData="M873.8,351.2C873.7,348.8 873.7,346.4 873.5,344C873.5,344.4 873.6,344.8 873.6,345.1C873.6,344.5 873.5,343.8 873.5,343.2C868.4,251.8 817.6,173.6 745.2,134.6C745.2,134.6 745.1,134.5 745,134.4C723,122.6 698.9,114.3 673.5,110.4C668.7,109.7 663.7,109.1 658.7,108.7C658.2,108.7 657.6,108.7 657.1,108.6C656.1,108.6 655.1,108.5 654.2,108.5C651,108.3 647.9,108.2 644.7,108.1C643.6,108.1 642.4,108.1 641.3,108.1C639,108.1 636.9,108.1 634.6,108.2C633.2,108.2 631.9,108.3 630.5,108.3C628.8,108.4 627.1,108.5 625.4,108.7C624.6,108.7 623.8,108.7 623,108.9C621,109 619,109.3 617.1,109.5C616.8,109.5 616.6,109.5 616.4,109.5C612.5,110 608.6,110.5 604.8,111.2C598.8,112.2 592.8,113.4 586.9,114.9C583.5,115.8 580.1,116.8 576.7,117.8C574.3,118.6 571.9,119.4 569.5,120.2C569.5,120.2 569.3,120.2 569.3,120.2C563.5,122.2 557.7,124.5 552.1,127C546.5,129.5 540.9,132.2 535.5,135.1C535.1,135.4 534.7,135.6 534.3,135.8C532.4,136.9 530.4,137.9 528.5,139C528.5,139 528.4,139 528.4,139.1C525.4,140.9 522.5,142.7 519.5,144.6C517.1,146.1 514.7,147.8 512.4,149.5C509.6,151.4 507,153.4 504.3,155.4C501.5,157.6 498.9,159.9 496.3,162.2C494.1,164 492,165.6 490,167.5C489.9,167.6 489.8,167.6 489.7,167.7C489.3,168.2 488.8,168.6 488.3,169C484.2,172.8 480.3,176.7 476.5,180.7C475.1,182.2 473.8,183.8 472.5,185.3C472,185.8 471.5,186.3 471.1,186.9C470.2,188 469.3,188.9 468.5,190C467,191.7 465.5,193.3 464.1,195C463.6,195.6 463.2,196.3 462.7,196.9C459.9,200.5 457,204.1 454.4,207.9C453,210 451.7,212.2 450.3,214.2C447.2,218.8 444.4,223.5 441.6,228.3C439.4,232.2 437.2,236.2 435.2,240.3C434.9,240.9 434.6,241.5 434.3,242C434.1,242.5 433.9,243 433.6,243.5C430.9,249.3 428.3,255.2 426,261.2C423.6,267.2 421.5,273.3 419.7,279.5C417.8,285.7 416.1,291.9 414.7,298.2C413.2,304.6 412,310.9 411.1,317.3C410.1,323.8 409.1,330.1 408.6,336.6C408.4,338.7 407.6,350.5 407.5,352.6C407.3,357 407.3,370.2 407.3,370.2C409.9,428.9 431.7,484.9 468.9,528.1C470.1,527.4 471.3,526.6 472.6,525.8C474.6,524.6 476.7,523.4 478.7,522.1C482.7,519.5 486.5,516.7 490.3,513.7C490.9,513.2 491.6,512.6 492.2,512.1C492.9,511.5 493.6,511 494.2,510.5C493,509.1 491.8,507.8 490.6,506.4C489.4,505 488.2,503.6 487,502.1C485.8,500.6 484.6,499.2 483.4,497.7C482.3,496.2 481.1,494.6 480,493.1C478.8,491.6 477.7,490 476.7,488.5C475.6,486.9 474.5,485.3 473.4,483.7C472.4,482.1 471.4,480.5 470.3,478.9C469.3,477.3 468.3,475.6 467.4,474C466.4,472.3 465.5,470.7 464.6,469C463.7,467.3 462.8,465.6 461.9,463.9C461.1,462.2 460.2,460.5 459.3,458.8C458.5,457.1 457.7,455.3 456.9,453.6C456.1,451.8 455.3,450.1 454.6,448.3C453.8,446.5 453.1,444.7 452.4,443C451.7,441.2 451,439.3 450.3,437.6C449.6,435.7 449,434 448.4,432.1C447.8,430.3 447.2,428.4 446.7,426.6C446.1,424.8 445.5,422.9 445,421C444.5,419.2 444,417.3 443.5,415.4C443.1,413.5 442.6,411.6 442.1,409.7C441.7,407.9 441.3,406 440.9,404.1C440.5,402.2 440.2,400.3 439.8,398.3C439.5,396.4 439.2,394.5 438.9,392.6C438.6,390.6 438.4,388.8 438.1,386.8C437.9,384.9 437.7,383 437.5,381C437.3,379.1 437.1,377.1 437,375.2C436.9,374.2 436.9,373.1 436.7,372.1C436.7,371.5 436.7,370.9 436.6,370.3V369.4C436.5,367.5 436.5,365.5 436.4,363.6C436.4,361.6 436.3,359.7 436.3,357.7C436.3,352.9 436.5,348 436.8,343.2C436.8,342.8 436.9,342.2 436.9,341.8C436.9,341.4 436.9,341 437,340.5C437.4,334.8 438,329.1 438.9,323.4C439.7,317.7 440.8,312.1 442,306.5C443.3,300.9 444.7,295.4 446.4,289.9C448,284.5 449.9,279.1 451.9,273.8C454,268.5 456.2,263.3 458.6,258.1C461.1,253 463.7,247.9 466.5,243.1C467,242.2 467.5,241.2 468.1,240.4C470.4,236.4 472.8,232.6 475.4,228.7C478.5,224.1 481.9,219.6 485.4,215.2C488.9,210.8 492.5,206.6 496.3,202.5C500,198.4 504,194.5 508.1,190.8C509.5,189.5 511,188.2 512.5,186.9C515.2,184.6 517.9,182.3 520.7,180.2C525,176.8 529.5,173.6 534,170.6C534.8,170.1 535.7,169.5 536.5,169C539,167.4 541.6,165.8 544.3,164.3C545.5,163.6 546.8,162.9 548,162.2C552.8,159.6 557.7,157.2 562.7,155C567.2,153 571.9,151.1 576.6,149.4L577.7,149C579.1,148.6 580.5,148 581.9,147.6C580.8,147.1 580.8,147.1 581.9,147.6C585.7,146.4 589.4,145.3 593.2,144.3C598.4,143 603.7,141.8 608.9,140.9C611.4,140.5 613.9,140.2 616.3,139.8C616.6,139.8 616.8,139.8 617,139.7C617.4,139.7 617.8,139.6 618.2,139.5C620.4,139.3 622.5,139.1 624.7,138.9H624.9C630.3,138.4 635.6,138.2 641,138.2C646.3,138.2 651.7,138.4 657,138.9C662.4,139.3 667.7,140 673,140.9C675.4,141.3 677.8,141.7 680.2,142.2C683.1,142.8 685.9,143.5 688.7,144.2C694,145.6 699.1,147.2 704.2,148.9C709.3,150.7 714.4,152.7 719.3,154.9C724.2,157 729.1,159.4 733.9,162.1C737.7,164.2 741.4,166.5 745,168.8C745.1,168.8 745.2,168.9 745.2,168.9C746.1,169.5 747,169.9 747.9,170.5C752.4,173.4 756.9,176.7 761.2,180C765.6,183.4 769.8,187 773.8,190.7C777.9,194.4 781.9,198.3 785.6,202.4C789.4,206.5 793.1,210.7 796.5,215.1C800,219.4 803.4,224 806.5,228.6C809.6,233.3 812.6,238 815.4,243C818.2,247.8 820.8,252.9 823.3,258C825.7,263.1 828,268.3 830,273.6C831.9,278.7 833.6,283.8 835.2,288.9C835.5,289.8 835.8,290.6 836,291.5C840.6,307.9 843.4,325.2 844.4,343C844.4,343.7 844.5,344.4 844.5,345C844.8,349.5 844.9,354.1 844.9,358.6C844.9,362.5 844.8,366.3 844.6,370.1C844.6,370.1 843.9,386.2 843.1,391.9C842.2,397.6 841.2,403.2 839.9,408.8C838.7,414.4 837.2,420 835.6,425.4C833.9,430.9 832.1,436.2 830,441.6C828,446.9 825.7,452.1 823.3,457.2C820.9,462.3 818.3,467.4 815.5,472.3C812.6,477.1 809.7,482 806.5,486.6C803.4,491.2 800,495.8 796.6,500.1C793.1,504.5 789.5,508.7 785.7,512.8C781.9,516.8 778,520.7 773.9,524.5C769.8,528.2 765.6,531.7 761.3,535.1C756.9,538.5 752.5,541.7 747.9,544.7C747.1,545.3 746.1,545.7 745.3,546.3C741.6,548.7 737.8,551 733.9,553.1C729.1,555.7 724.2,558.1 719.3,560.3C714.4,562.5 709.3,564.5 704.2,566.3C699.9,567.8 695.4,569.1 690.9,570.3C675.6,585.3 658.9,598.2 641,608.8C677.6,608.8 713.2,599.4 745.2,582.1C767.3,570.2 787.8,554.6 805.5,535.4C846.7,491.2 870.9,432.2 873.7,370.1C873.7,369.3 873.7,368.3 873.7,367.5C873.8,364.4 873.9,361.3 873.9,358.3C873.9,355.9 873.8,353.5 873.7,351.1L873.8,351.2Z" + android:fillColor="#000000"/> + </group> +</vector> diff --git a/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml b/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@mipmap/ic_launcher_foreground" /> -</adaptive-icon> -\ No newline at end of file diff --git a/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml b/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@mipmap/ic_launcher_foreground" /> -</adaptive-icon> -\ No newline at end of file diff --git a/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_pos_logo.xml b/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_pos_logo.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ This file is part of GNU Taler + ~ (C) 2026 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_taler_pos_logo_background"/> + <foreground android:drawable="@drawable/ic_taler_pos_logo_foreground"/> + <monochrome android:drawable="@drawable/ic_taler_pos_logo_monochrome"/> +</adaptive-icon> diff --git a/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_pos_logo_round.xml b/merchant-terminal/src/main/res/mipmap-anydpi-v26/ic_taler_pos_logo_round.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ This file is part of GNU Taler + ~ (C) 2026 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_taler_pos_logo_background"/> + <foreground android:drawable="@drawable/ic_taler_pos_logo_foreground"/> + <monochrome android:drawable="@drawable/ic_taler_pos_logo_monochrome"/> +</adaptive-icon> diff --git a/merchant-terminal/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/merchant-terminal/src/main/res/mipmap-hdpi/ic_launcher_foreground.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_logo.png b/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_logo.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_logo_round.png b/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_logo_round.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_pos_logo.webp b/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_pos_logo.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_pos_logo_round.webp b/merchant-terminal/src/main/res/mipmap-hdpi/ic_taler_pos_logo_round.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/merchant-terminal/src/main/res/mipmap-mdpi/ic_launcher_foreground.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_logo.png b/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_logo.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_logo_round.png b/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_logo_round.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_pos_logo.webp b/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_pos_logo.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_pos_logo_round.webp b/merchant-terminal/src/main/res/mipmap-mdpi/ic_taler_pos_logo_round.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/merchant-terminal/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_logo.png b/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_logo.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png b/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_pos_logo.webp b/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_pos_logo.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_pos_logo_round.webp b/merchant-terminal/src/main/res/mipmap-xhdpi/ic_taler_pos_logo_round.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_logo.png b/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_logo.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png b/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_pos_logo.webp b/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_pos_logo.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_pos_logo_round.webp b/merchant-terminal/src/main/res/mipmap-xxhdpi/ic_taler_pos_logo_round.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png b/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png b/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_pos_logo.webp b/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_pos_logo.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_pos_logo_round.webp b/merchant-terminal/src/main/res/mipmap-xxxhdpi/ic_taler_pos_logo_round.webp Binary files differ. diff --git a/merchant-terminal/src/main/res/values-de/strings.xml b/merchant-terminal/src/main/res/values-de/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name_short">\"Merchant Terminal\"</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Normale Bestellung</string> <string name="menu_history">Verlauf</string> @@ -49,7 +49,7 @@ \n \n%2$s</string> <string name="toast_back_to_exit">Klicken Sie zum Beenden erneut auf «Zurück»</string> - <string name="app_name">GNU Taler Point-of-Sale</string> + <string name="app_name">Taler POS</string> <string name="config_label">Händlereinstellungen</string> <string name="config_username">Benutzername</string> <string name="config_password">Passwort</string> @@ -100,7 +100,7 @@ <string name="config_qr_label">QR-Code-Konfiguration</string> <string name="scan_qr_hint">Scannen Sie den QR-Code, den das Taler Merchant Backoffice erzeugt hat</string> <string name="duration">Gültigkeitszeitraum</string> - <string name="token_validity_deadline">Gültigkeitszeitraum des Passworts:</string> + <string name="token_validity_deadline">Gültigkeitsfrist des Passworts:</string> <string name="expires_on">Gültig bis…</string> <string name="never_expires">Unbegrenzte Gültigkeit</string> <string name="no_deadline_set">Es wurde keine Verfallsfrist festgelegt</string> @@ -118,18 +118,22 @@ <string name="settings_language_label">Sprache</string> <string name="settings_language_hint">App-Sprache</string> <string name="settings_language_system_default">Systemvorgabe</string> - <string name="settings_subtitle">Wählen Sie die App-Sprache und verwalten Sie die Zugangsdaten der Händlerinstanz.</string> + <string name="settings_subtitle">Wählen Sie die App-Sprache und verwalten Sie die Anmeldeeinstellungen.</string> <string name="settings_language_description">Wird sofort auf die App-Oberfläche angewendet.</string> <string name="settings_initial_order_label">Startbildschirm für Bestellungen</string> <string name="settings_initial_order_hint">Standard-Bestellmodus</string> <string name="settings_initial_order_description">Wird beim Start der App oder nach dem Laden der Konfiguration angezeigt.</string> - <string name="settings_instance_title">Gespeicherte Instanz</string> - <string name="settings_instance_description">Händlerportal-URL, Benutzername, Token und weitere instanzspezifische Einstellungen ändern.</string> - <string name="settings_instance_button">Gespeicherte Instanz ändern</string> + <string name="settings_instance_title">Anmeldeeinstellungen</string> + <string name="settings_instance_description">Händlerportal-URL, Benutzername, Passwort und weitere Einstellungen ändern.</string> + <string name="settings_instance_button">Anmeldeeinstellungen ändern</string> <string name="settings_logout_button">Abmelden</string> <string name="amount_entry_charge">Berechnen</string> <string name="amount_entry_create_order_charge">Bestellung erstellen (Betrag berechnen)</string> <string name="amount_entry_backspace">Löschen</string> + <string name="product_wrong_currency">Falsche Währung</string> + <string name="force_delete_dialog_title">Löschen der Bestellung erzwingen?</string> + <string name="force_delete_dialog_message">Diese Bestellung wurde von einer Wallet beansprucht. Durch das erzwungene Löschen werden alle Bestandssperren aufgehoben und die Wallet kann die Zahlung nicht mehr abschließen.</string> + <string name="force_delete_dialog_confirm">Löschen erzwingen</string> <string-array name="settings_language_values"> <item></item> <item>en</item> diff --git a/merchant-terminal/src/main/res/values-es/strings.xml b/merchant-terminal/src/main/res/values-es/strings.xml @@ -68,8 +68,8 @@ <string name="error_cancelled">Pago cancelado</string> <string name="error_history">Error obteniendo el historial de pedidos</string> <string name="toast_back_to_exit">Haz clic en «atrás» de nuevo para salir</string> - <string name="app_name">GNU Taler Punto de Venta</string> - <string name="app_name_short">Terminal de comercio</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="config_url">Configuración URL</string> <string name="config_error_network">Error: No se pudo conectar al servidor de configuración</string> <string name="config_error_currency">Error: Producto %1$s tiene una divisa %2$s, pero %3$s esperada</string> @@ -95,15 +95,15 @@ <string name="settings_language_label">Idioma</string> <string name="settings_language_hint">Idioma de la app</string> <string name="settings_language_system_default">Predeterminado del sistema</string> - <string name="settings_subtitle">Elige el idioma de la app y gestiona las credenciales de la instancia del comercio.</string> + <string name="settings_subtitle">Elige el idioma de la app y gestiona los ajustes de inicio de sesión.</string> <string name="settings_app_title">Ajustes de la app</string> <string name="settings_language_description">Se aplica inmediatamente a la interfaz de la app.</string> <string name="settings_initial_order_label">Pantalla inicial de pedido</string> <string name="settings_initial_order_hint">Modo de pedido predeterminado</string> <string name="settings_initial_order_description">Se muestra al iniciar la app o después de cargar la configuración.</string> - <string name="settings_instance_title">Instancia guardada</string> - <string name="settings_instance_description">Cambia la URL del portal del comercio, el nombre de usuario, el token y otros ajustes de la instancia.</string> - <string name="settings_instance_button">Modificar instancia guardada</string> + <string name="settings_instance_title">Ajustes de inicio de sesión</string> + <string name="settings_instance_description">Cambia la URL del portal del comercio, el nombre de usuario, la contraseña y otros ajustes.</string> + <string name="settings_instance_button">Modificar ajustes de inicio de sesión</string> <string name="settings_logout_button">Cerrar sesión</string> <string name="amount_entry_label">Importe</string> <string name="amount_entry_charge">Cobrar</string> @@ -124,7 +124,7 @@ <string name="forever">Para siempre</string> <string name="custom_duration">Duración personalizada</string> <string name="duration">Duración</string> - <string name="token_validity_deadline">Fecha límite de validez del token:</string> + <string name="token_validity_deadline">Fecha límite de validez de la contraseña:</string> <string name="expires_on">Caduca el…</string> <string name="never_expires">Nunca caduca</string> <string name="no_deadline_set">Sin fecha límite</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Error: no se pudo crear el pedido</string> <string name="error_inventory_unavailable">Error: no hay inventario suficiente disponible</string> <string name="error_delete_order">Error: no se pudo eliminar el pedido</string> + <string name="product_wrong_currency">Moneda incorrecta</string> + <string name="force_delete_dialog_title">¿Forzar la eliminación del pedido?</string> + <string name="force_delete_dialog_message">Este pedido ha sido reclamado por una cartera. Forzar su eliminación liberará cualquier bloqueo de inventario e impedirá que la cartera complete el pago.</string> + <string name="force_delete_dialog_confirm">Forzar eliminación</string> </resources> diff --git a/merchant-terminal/src/main/res/values-fi/strings.xml b/merchant-terminal/src/main/res/values-fi/strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">GNU Taler -myyntipiste</string> - <string name="app_name_short">Kauppiasterminaali</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Tilaukset</string> <string name="menu_history">Historia</string> @@ -89,15 +89,15 @@ <string name="settings_language_label">Kieli</string> <string name="settings_language_hint">Sovelluksen kieli</string> <string name="settings_language_system_default">Järjestelmän oletus</string> - <string name="settings_subtitle">Valitse sovelluksen kieli ja hallitse kauppiasinstanssin tunnistetietoja.</string> + <string name="settings_subtitle">Valitse sovelluksen kieli ja hallitse kirjautumisasetuksia.</string> <string name="settings_app_title">Sovelluksen asetukset</string> <string name="settings_language_description">Käytetään heti sovelluksen käyttöliittymässä.</string> <string name="settings_initial_order_label">Aloitusnäkymä tilauksille</string> <string name="settings_initial_order_hint">Oletustilaustapa</string> <string name="settings_initial_order_description">Näytetään, kun sovellus käynnistyy tai kun määritykset on ladattu.</string> - <string name="settings_instance_title">Tallennettu instanssi</string> - <string name="settings_instance_description">Muuta kauppiasportaalin URL-osoitetta, käyttäjätunnusta, tokenia ja muita instanssin asetuksia.</string> - <string name="settings_instance_button">Muokkaa tallennettua instanssia</string> + <string name="settings_instance_title">Kirjautumisasetukset</string> + <string name="settings_instance_description">Muuta kauppiasportaalin URL-osoitetta, käyttäjätunnusta, salasanaa ja muita asetuksia.</string> + <string name="settings_instance_button">Muokkaa kirjautumisasetuksia</string> <string name="settings_logout_button">Kirjaudu ulos</string> <string name="config_merchant_url">Kauppiasportaalin URL</string> <string name="amount_entry_label">Summa</string> @@ -116,13 +116,13 @@ <string name="config_old_label">JSON-tiedosto (vanha)</string> <string name="config_setup_password">Salasana</string> <string name="config_old_deprecation">Tämä määritystapa on vanhentunut, käytä uutta kauppias-API-määritystä.</string> - <string name="config_token">Käyttöoikeustunnus</string> + <string name="config_token">Salasana</string> <string name="config_qr_label">QR-määritys</string> <string name="scan_qr_hint">Skannaa QR-koodi kauppiaan backofficesta</string> <string name="forever">Ikuisesti</string> <string name="custom_duration">Mukautettu kesto</string> <string name="duration">Kesto</string> - <string name="token_validity_deadline">Tunnuksen voimassaolon määräaika:</string> + <string name="token_validity_deadline">Salasanan voimassaolon määräaika:</string> <string name="expires_on">Vanhenee…</string> <string name="never_expires">Ei vanhene koskaan</string> <string name="no_deadline_set">Määräaikaa ei ole asetettu</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Virhe: tilausta ei voitu luoda</string> <string name="error_inventory_unavailable">Virhe: varastoa ei ole riittävästi saatavilla</string> <string name="error_delete_order">Virhe: tilausta ei voitu poistaa</string> + <string name="product_wrong_currency">Väärä valuutta</string> + <string name="force_delete_dialog_title">Pakotetaanko tilauksen poistaminen?</string> + <string name="force_delete_dialog_message">Lompakko on lunastanut tämän tilauksen. Pakotettu poistaminen vapauttaa kaikki varastovaraukset ja estää lompakkoa suorittamasta maksua.</string> + <string name="force_delete_dialog_confirm">Pakota poisto</string> </resources> diff --git a/merchant-terminal/src/main/res/values-fr/strings.xml b/merchant-terminal/src/main/res/values-fr/strings.xml @@ -11,8 +11,8 @@ <string name="refund_confirm">Approuver le remboursement</string> <string name="refund_error_backend">Erreur lors du traitement du remboursement</string> <string name="menu_settings">Paramètres</string> - <string name="app_name">GNU Taler Point de vente</string> - <string name="app_name_short">Terminal marchand</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="menu_order">Commandes</string> <string name="order_label_title">Commande numéro %s</string> <string name="order_restart">Effacer</string> @@ -96,7 +96,7 @@ <string name="forever">Toujours</string> <string name="custom_duration">Durée personnalisée</string> <string name="duration">Durée</string> - <string name="token_validity_deadline">Date limite de validité du jeton :</string> + <string name="token_validity_deadline">Date limite de validité du mot de passe :</string> <string name="expires_on">Expire le …</string> <string name="no_deadline_set">Aucune date limite</string> <string name="pick_date">Choisir la date</string> @@ -108,15 +108,15 @@ <string name="settings_language_label">Langue</string> <string name="settings_language_hint">Langue de l’application</string> <string name="settings_language_system_default">Paramètre système</string> - <string name="settings_subtitle">Choisissez la langue de l’application et gérez les identifiants de l’instance commerçant.</string> + <string name="settings_subtitle">Choisissez la langue de l’application et gérez les paramètres de connexion.</string> <string name="settings_app_title">Paramètres de l’application</string> <string name="settings_language_description">S’applique immédiatement à l’interface de l’application.</string> <string name="settings_initial_order_label">Écran de commande initial</string> <string name="settings_initial_order_hint">Mode de commande par défaut</string> <string name="settings_initial_order_description">Affiché au démarrage de l’application ou après le chargement de la configuration.</string> - <string name="settings_instance_title">Instance enregistrée</string> - <string name="settings_instance_description">Modifier l’URL du portail commerçant, le nom d’utilisateur, le jeton et les autres paramètres de l’instance.</string> - <string name="settings_instance_button">Modifier l’instance enregistrée</string> + <string name="settings_instance_title">Paramètres de connexion</string> + <string name="settings_instance_description">Modifier l’URL du portail commerçant, le nom d’utilisateur, le mot de passe et les autres paramètres.</string> + <string name="settings_instance_button">Modifier les paramètres de connexion</string> <string name="settings_logout_button">Se déconnecter</string> <string name="amount_entry_label">Montant</string> <string name="amount_entry_charge">Encaisser</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Erreur : impossible de créer la commande</string> <string name="error_inventory_unavailable">Erreur : stock disponible insuffisant</string> <string name="error_delete_order">Erreur : impossible de supprimer la commande</string> + <string name="product_wrong_currency">Devise incorrecte</string> + <string name="force_delete_dialog_title">Forcer la suppression de la commande ?</string> + <string name="force_delete_dialog_message">Cette commande a été réclamée par un portefeuille. Forcer sa suppression libérera tous les verrouillages de stock et empêchera le portefeuille de finaliser le paiement.</string> + <string name="force_delete_dialog_confirm">Forcer la suppression</string> </resources> diff --git a/merchant-terminal/src/main/res/values-it/strings.xml b/merchant-terminal/src/main/res/values-it/strings.xml @@ -49,8 +49,8 @@ <string name="refund_confirm">Approva rimborso</string> <string name="refund_error_already_refunded">Già rimborsato</string> <string name="error_payment">Errore: Nessun pagamento ricevuto</string> - <string name="app_name">GNU Taler Punto vendita</string> - <string name="app_name_short">Terminale commerciante</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="menu_amount_entry">Ordine per importo</string> <string name="menu_history">Storico</string> <string name="menu_reload">Ricarica inventario</string> @@ -77,15 +77,15 @@ <string name="settings_language_label">Lingua</string> <string name="settings_language_hint">Lingua dell’app</string> <string name="settings_language_system_default">Predefinita di sistema</string> - <string name="settings_subtitle">Scegli la lingua dell’app e gestisci le credenziali dell’istanza commerciante.</string> + <string name="settings_subtitle">Scegli la lingua dell’app e gestisci le impostazioni di accesso.</string> <string name="settings_app_title">Impostazioni app</string> <string name="settings_language_description">Applicata immediatamente all’interfaccia dell’app.</string> <string name="settings_initial_order_label">Schermata iniziale ordine</string> <string name="settings_initial_order_hint">Modalità ordine predefinita</string> <string name="settings_initial_order_description">Mostrata all’avvio dell’app o dopo il caricamento della configurazione.</string> - <string name="settings_instance_title">Istanza salvata</string> - <string name="settings_instance_description">Modifica URL del portale commerciante, nome utente, token e altre impostazioni dell’istanza.</string> - <string name="settings_instance_button">Modifica istanza salvata</string> + <string name="settings_instance_title">Impostazioni di accesso</string> + <string name="settings_instance_description">Modifica l’URL del portale commerciante, il nome utente, la password e altre impostazioni.</string> + <string name="settings_instance_button">Modifica impostazioni di accesso</string> <string name="settings_logout_button">Disconnetti</string> <string name="config_label">Impostazioni commerciante</string> <string name="config_old_label">File JSON (vecchio)</string> @@ -94,7 +94,7 @@ <string name="config_url">URL configurazione</string> <string name="config_merchant_url">URL portale commerciante</string> <string name="config_username">Nome utente</string> - <string name="config_token">Token di accesso</string> + <string name="config_token">Password</string> <string name="config_ok">Connetti</string> <string name="config_auth_error">Errore: nome utente o password non validi</string> <string name="config_error_network">Errore: impossibile connettersi al server di configurazione</string> @@ -111,7 +111,7 @@ <string name="forever">Per sempre</string> <string name="custom_duration">Durata personalizzata</string> <string name="duration">Durata</string> - <string name="token_validity_deadline">Scadenza validità token:</string> + <string name="token_validity_deadline">Scadenza validità password:</string> <string name="expires_on">Scade il…</string> <string name="never_expires">Non scade mai</string> <string name="no_deadline_set">Nessuna scadenza impostata</string> @@ -145,6 +145,10 @@ <string-array name="settings_language_labels"><item>@string/settings_language_system_default</item><item>English</item><item>Deutsch</item><item>Español</item><item>Suomi</item><item>Français</item><item>Italiano</item><item>Русский</item><item>Svenska</item><item>Türkçe</item><item>Українська</item><item>עברית</item></string-array> <string name="product_unavailable">Non disponibile</string> <string name="product_out_of_stock">Esaurito</string> + <string name="product_wrong_currency">Valuta errata</string> + <string name="force_delete_dialog_title">Eliminare forzatamente l’ordine?</string> + <string name="force_delete_dialog_message">Questo ordine è stato reclamato da un portafoglio. L’eliminazione forzata rilascerà tutti i blocchi dell’inventario e impedirà al portafoglio di completare il pagamento.</string> + <string name="force_delete_dialog_confirm">Elimina forzatamente</string> <string name="order_delete">Cancella ordine</string> <string name="error_order_creation">Errore: impossibile creare l’ordine</string> <string name="error_inventory_unavailable">Errore: inventario disponibile insufficiente</string> diff --git a/merchant-terminal/src/main/res/values-iw/strings.xml b/merchant-terminal/src/main/res/values-iw/strings.xml @@ -2,8 +2,8 @@ <resources> <string name="menu_settings">הגדרות</string> <string name="config_save_password">שמירת הסיסמה</string> - <string name="app_name">נקודת מכירה GNU Taler</string> - <string name="app_name_short">מסוף סוחר</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">הזמנה לפי מלאי</string> <string name="menu_amount_entry">הזמנה לפי סכום</string> @@ -36,15 +36,15 @@ <string name="settings_language_label">שפה</string> <string name="settings_language_hint">שפת האפליקציה</string> <string name="settings_language_system_default">ברירת מחדל של המערכת</string> - <string name="settings_subtitle">בחרו את שפת האפליקציה ונהלו את פרטי הגישה של מופע הסוחר.</string> + <string name="settings_subtitle">בחרו את שפת האפליקציה ונהלו את הגדרות ההתחברות.</string> <string name="settings_app_title">הגדרות אפליקציה</string> <string name="settings_language_description">מוחל מיד על ממשק האפליקציה.</string> <string name="settings_initial_order_label">מסך הזמנה התחלתי</string> <string name="settings_initial_order_hint">מצב הזמנה ברירת מחדל</string> <string name="settings_initial_order_description">מוצג כשהאפליקציה מופעלת או אחרי טעינת התצורה.</string> - <string name="settings_instance_title">מופע שמור</string> - <string name="settings_instance_description">שינוי כתובת פורטל הסוחר, שם משתמש, אסימון והגדרות נוספות של המופע.</string> - <string name="settings_instance_button">שינוי מופע שמור</string> + <string name="settings_instance_title">הגדרות התחברות</string> + <string name="settings_instance_description">שינוי כתובת פורטל הסוחר, שם המשתמש, הסיסמה והגדרות נוספות.</string> + <string name="settings_instance_button">שינוי הגדרות התחברות</string> <string name="settings_logout_button">התנתק</string> <string name="config_old_label">קובץ JSON (ישן)</string> <string name="config_setup_password">סיסמה</string> @@ -54,7 +54,7 @@ <string name="config_username">שם משתמש</string> <string name="config_password">סיסמה</string> <string name="config_manual_label">תצורה ידנית</string> - <string name="config_token">אסימון גישה</string> + <string name="config_token">סיסמה</string> <string name="config_ok">התחבר</string> <string name="config_auth_error">שגיאה: שם משתמש או סיסמה לא תקינים</string> <string name="config_error_network">שגיאה: לא ניתן להתחבר לשרת התצורה</string> @@ -74,7 +74,7 @@ <string name="forever">לתמיד</string> <string name="custom_duration">משך מותאם</string> <string name="duration">משך</string> - <string name="token_validity_deadline">מועד תוקף האסימון:</string> + <string name="token_validity_deadline">מועד תפוגת הסיסמה:</string> <string name="expires_on">פג בתאריך…</string> <string name="never_expires">לעולם לא פג</string> <string name="no_deadline_set">לא נקבע מועד תפוגה</string> @@ -136,4 +136,8 @@ <string name="error_order_creation">שגיאה: לא ניתן ליצור הזמנה</string> <string name="error_inventory_unavailable">שגיאה: אין מספיק מלאי זמין</string> <string name="error_delete_order">שגיאה: לא ניתן למחוק את ההזמנה</string> + <string name="product_wrong_currency">מטבע שגוי</string> + <string name="force_delete_dialog_title">לכפות את מחיקת ההזמנה?</string> + <string name="force_delete_dialog_message">ההזמנה נתבעה על ידי ארנק. מחיקה כפויה שלה תשחרר את כל נעילות המלאי ותמנע מהארנק להשלים את התשלום.</string> + <string name="force_delete_dialog_confirm">מחיקה כפויה</string> </resources> diff --git a/merchant-terminal/src/main/res/values-ru/strings.xml b/merchant-terminal/src/main/res/values-ru/strings.xml @@ -29,8 +29,8 @@ <string name="refund_abort">Отменить</string> <string name="refund_complete">Получено</string> <string name="refund_confirm">Подтвердить возврат</string> - <string name="app_name">GNU Taler Точка Продажи</string> - <string name="app_name_short">Терминал продавца</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Заказы</string> <string name="menu_history">История</string> @@ -89,15 +89,15 @@ <string name="settings_language_label">Язык</string> <string name="settings_language_hint">Язык приложения</string> <string name="settings_language_system_default">Системный</string> - <string name="settings_subtitle">Выберите язык приложения и управляйте учетными данными экземпляра продавца.</string> + <string name="settings_subtitle">Выберите язык приложения и управляйте настройками входа.</string> <string name="settings_app_title">Настройки приложения</string> <string name="settings_language_description">Применяется к интерфейсу приложения сразу.</string> <string name="settings_initial_order_label">Начальный экран заказа</string> <string name="settings_initial_order_hint">Режим заказа по умолчанию</string> <string name="settings_initial_order_description">Показывается при запуске приложения или после загрузки конфигурации.</string> - <string name="settings_instance_title">Сохраненный экземпляр</string> - <string name="settings_instance_description">Изменить URL портала продавца, имя пользователя, токен и другие настройки экземпляра.</string> - <string name="settings_instance_button">Изменить сохраненный экземпляр</string> + <string name="settings_instance_title">Настройки входа</string> + <string name="settings_instance_description">Изменить URL портала продавца, имя пользователя, пароль и другие настройки.</string> + <string name="settings_instance_button">Изменить настройки входа</string> <string name="settings_logout_button">Выйти</string> <string name="config_merchant_url">URL портала продавца</string> <string name="amount_entry_label">Сумма</string> @@ -116,13 +116,13 @@ <string name="config_old_label">Файл JSON (старый)</string> <string name="config_setup_password">Пароль</string> <string name="config_old_deprecation">Этот способ настройки устарел, используйте новую настройку через API продавца.</string> - <string name="config_token">Токен доступа</string> + <string name="config_token">Пароль</string> <string name="config_qr_label">QR-конфигурация</string> <string name="scan_qr_hint">Отсканируйте QR-код из бэк-офиса продавца</string> <string name="forever">Навсегда</string> <string name="custom_duration">Пользовательская длительность</string> <string name="duration">Длительность</string> - <string name="token_validity_deadline">Срок действия токена:</string> + <string name="token_validity_deadline">Срок действия пароля:</string> <string name="expires_on">Истекает…</string> <string name="never_expires">Никогда не истекает</string> <string name="no_deadline_set">Срок не задан</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Ошибка: не удалось создать заказ</string> <string name="error_inventory_unavailable">Ошибка: недостаточно доступного инвентаря</string> <string name="error_delete_order">Ошибка: не удалось удалить заказ</string> + <string name="product_wrong_currency">Неверная валюта</string> + <string name="force_delete_dialog_title">Принудительно удалить заказ?</string> + <string name="force_delete_dialog_message">Этот заказ был заявлен кошельком. Принудительное удаление снимет все блокировки запасов и не позволит кошельку завершить оплату.</string> + <string name="force_delete_dialog_confirm">Удалить принудительно</string> </resources> diff --git a/merchant-terminal/src/main/res/values-sv/strings.xml b/merchant-terminal/src/main/res/values-sv/strings.xml @@ -64,8 +64,8 @@ <string name="error_cancelled">Betalning Avbruten</string> <string name="error_history">Det gick inte att hämta orderhistoriken</string> <string name="toast_back_to_exit">Klicka på «tillbaka» igen för att avsluta</string> - <string name="app_name">GNU Taler försäljningsställe</string> - <string name="app_name_short">Säljarens terminal</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="config_label">Säljar-inställningar</string> <string name="config_manual_label">Manuell konfiguration</string> <string name="config_auth_error">Fel: Ogiltigt användarnamn eller lösenord</string> @@ -85,15 +85,15 @@ <string name="settings_language_label">Språk</string> <string name="settings_language_hint">Appspråk</string> <string name="settings_language_system_default">Systemstandard</string> - <string name="settings_subtitle">Välj appspråk och hantera inloggningsuppgifter för handlarinstansen.</string> + <string name="settings_subtitle">Välj appspråk och hantera inloggningsinställningar.</string> <string name="settings_app_title">Appinställningar</string> <string name="settings_language_description">Tillämpas direkt på appens gränssnitt.</string> <string name="settings_initial_order_label">Startskärm för beställning</string> <string name="settings_initial_order_hint">Standardläge för beställning</string> <string name="settings_initial_order_description">Visas när appen startar eller efter att konfigurationen har laddats.</string> - <string name="settings_instance_title">Sparad instans</string> - <string name="settings_instance_description">Ändra handlarportalens URL, användarnamn, token och andra instansspecifika inställningar.</string> - <string name="settings_instance_button">Ändra sparad instans</string> + <string name="settings_instance_title">Inloggningsinställningar</string> + <string name="settings_instance_description">Ändra handlarportalens URL, användarnamn, lösenord och andra inställningar.</string> + <string name="settings_instance_button">Ändra inloggningsinställningar</string> <string name="settings_logout_button">Logga ut</string> <string name="config_merchant_url">Handlarportalens URL</string> <string name="amount_entry_label">Belopp</string> @@ -115,13 +115,13 @@ <string name="config_old_label">JSON-fil (gammal)</string> <string name="config_setup_password">Lösenord</string> <string name="config_old_deprecation">Den här konfigurationsmetoden är föråldrad, använd den nya konfigurationen via handlar-API:t.</string> - <string name="config_token">Åtkomsttoken</string> + <string name="config_token">Lösenord</string> <string name="config_qr_label">QR-konfiguration</string> <string name="scan_qr_hint">Skanna QR-koden från handlarens backoffice</string> <string name="forever">För alltid</string> <string name="custom_duration">Anpassad varaktighet</string> <string name="duration">Varaktighet</string> - <string name="token_validity_deadline">Tokenens giltighetstid:</string> + <string name="token_validity_deadline">Lösenordets giltighetstid:</string> <string name="expires_on">Går ut…</string> <string name="never_expires">Går aldrig ut</string> <string name="no_deadline_set">Ingen tidsgräns angiven</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Fel: kunde inte skapa bestallning</string> <string name="error_inventory_unavailable">Fel: inte tillrackligt lager tillgangligt</string> <string name="error_delete_order">Fel: kunde inte ta bort bestallning</string> + <string name="product_wrong_currency">Fel valuta</string> + <string name="force_delete_dialog_title">Tvinga borttagning av beställningen?</string> + <string name="force_delete_dialog_message">Den här beställningen har reserverats av en plånbok. En tvingad borttagning frigör alla lagerlås och hindrar plånboken från att slutföra betalningen.</string> + <string name="force_delete_dialog_confirm">Tvinga borttagning</string> </resources> diff --git a/merchant-terminal/src/main/res/values-tr/strings.xml b/merchant-terminal/src/main/res/values-tr/strings.xml @@ -55,8 +55,8 @@ <string name="history_status_refunded">Geri ödendi</string> <string name="history_show_payment">Ödemeyi göster</string> <string name="history_show_refund">Geri ödemeyi göster</string> - <string name="app_name">GNU Taler Satış Noktası</string> - <string name="app_name_short">Ticari Terminal</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Siparişler</string> <string name="menu_settings">Ayarlar</string> @@ -96,15 +96,15 @@ <string name="settings_language_label">Dil</string> <string name="settings_language_hint">Uygulama dili</string> <string name="settings_language_system_default">Sistem varsayılanı</string> - <string name="settings_subtitle">Uygulama dilini seçin ve satıcı örneği kimlik bilgilerini yönetin.</string> + <string name="settings_subtitle">Uygulama dilini seçin ve oturum açma ayarlarını yönetin.</string> <string name="settings_app_title">Uygulama ayarları</string> <string name="settings_language_description">Uygulama arayüzüne hemen uygulanır.</string> <string name="settings_initial_order_label">Başlangıç sipariş ekranı</string> <string name="settings_initial_order_hint">Varsayılan sipariş modu</string> <string name="settings_initial_order_description">Uygulama başlatıldığında veya yapılandırma yüklendikten sonra gösterilir.</string> - <string name="settings_instance_title">Kayıtlı örnek</string> - <string name="settings_instance_description">Satıcı portalı URL’sini, kullanıcı adını, tokeni ve diğer örneğe özel ayarları değiştirin.</string> - <string name="settings_instance_button">Kayıtlı örneği değiştir</string> + <string name="settings_instance_title">Oturum açma ayarları</string> + <string name="settings_instance_description">Satıcı portalı URL’sini, kullanıcı adını, şifreyi ve diğer ayarları değiştirin.</string> + <string name="settings_instance_button">Oturum açma ayarlarını değiştir</string> <string name="settings_logout_button">Oturumu kapat</string> <string name="amount_entry_label">Tutar</string> <string name="amount_entry_charge">Ücret al</string> @@ -123,7 +123,7 @@ <string name="forever">Süresiz</string> <string name="custom_duration">Özel süre</string> <string name="duration">Süre</string> - <string name="token_validity_deadline">Token geçerlilik son tarihi:</string> + <string name="token_validity_deadline">Şifre geçerlilik son tarihi:</string> <string name="expires_on">Bitiş tarihi…</string> <string name="never_expires">Asla sona ermez</string> <string name="no_deadline_set">Son tarih ayarlanmadı</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Hata: sipariş oluşturulamadı</string> <string name="error_inventory_unavailable">Hata: yeterli envanter mevcut değil</string> <string name="error_delete_order">Hata: sipariş silinemedi</string> + <string name="product_wrong_currency">Yanlış para birimi</string> + <string name="force_delete_dialog_title">Sipariş zorla silinsin mi?</string> + <string name="force_delete_dialog_message">Bu sipariş bir cüzdan tarafından talep edildi. Zorla silinmesi tüm envanter kilitlerini kaldıracak ve cüzdanın ödemeyi tamamlamasını engelleyecektir.</string> + <string name="force_delete_dialog_confirm">Zorla sil</string> </resources> diff --git a/merchant-terminal/src/main/res/values-uk/strings.xml b/merchant-terminal/src/main/res/values-uk/strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">GNU Taler Point-of-Sale</string> - <string name="app_name_short">Термінал продавця</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Замовлення</string> <string name="menu_history">Історія</string> @@ -23,7 +23,7 @@ <string name="config_url">URL конфігурації</string> <string name="config_password">Пароль</string> <string name="config_merchant_url">URL продавця</string> - <string name="config_token">пароль</string> + <string name="config_token">Пароль</string> <string name="config_ok">Отримати конфігурацію</string> <string name="config_error_network">Помилка: не вдалося підключитися до сервера конфігурації</string> <string name="config_error_malformed">Помилка: конфігураційний JSON некоректний</string> @@ -95,15 +95,15 @@ <string name="settings_language_label">Мова</string> <string name="settings_language_hint">Мова застосунку</string> <string name="settings_language_system_default">Системна</string> - <string name="settings_subtitle">Виберіть мову застосунку та керуйте обліковими даними екземпляра продавця.</string> + <string name="settings_subtitle">Виберіть мову застосунку та керуйте налаштуваннями входу.</string> <string name="settings_app_title">Налаштування застосунку</string> <string name="settings_language_description">Застосовується до інтерфейсу застосунку негайно.</string> <string name="settings_initial_order_label">Початковий екран замовлення</string> <string name="settings_initial_order_hint">Тип замовлення за замовчуванням</string> <string name="settings_initial_order_description">Показується під час запуску застосунку або після завантаження конфігурації.</string> - <string name="settings_instance_title">Збережений екземпляр</string> - <string name="settings_instance_description">Змінити URL порталу продавця, ім’я користувача, токен та інші налаштування екземпляра.</string> - <string name="settings_instance_button">Змінити збережений екземпляр</string> + <string name="settings_instance_title">Налаштування входу</string> + <string name="settings_instance_description">Змінити URL порталу продавця, ім’я користувача, пароль та інші налаштування.</string> + <string name="settings_instance_button">Змінити налаштування входу</string> <string name="settings_logout_button">Вийти</string> <string name="amount_entry_label">Сума</string> <string name="amount_entry_charge">Стягнути</string> @@ -124,7 +124,7 @@ <string name="forever">Назавжди</string> <string name="custom_duration">Власна тривалість</string> <string name="duration">Тривалість</string> - <string name="token_validity_deadline">Кінцевий термін дії токена:</string> + <string name="token_validity_deadline">Кінцевий термін дії пароля:</string> <string name="expires_on">Закінчується…</string> <string name="never_expires">Ніколи не закінчується</string> <string name="no_deadline_set">Термін не встановлено</string> @@ -138,4 +138,8 @@ <string name="error_order_creation">Помилка: не вдалося створити замовлення</string> <string name="error_inventory_unavailable">Помилка: недостатньо доступного інвентарю</string> <string name="error_delete_order">Помилка: не вдалося видалити замовлення</string> + <string name="product_wrong_currency">Неправильна валюта</string> + <string name="force_delete_dialog_title">Примусово видалити замовлення?</string> + <string name="force_delete_dialog_message">Це замовлення було заявлено гаманцем. Примусове видалення зніме всі блокування запасів і не дозволить гаманцю завершити оплату.</string> + <string name="force_delete_dialog_confirm">Примусово видалити</string> </resources> diff --git a/merchant-terminal/src/main/res/values/ic_taler_pos_logo_background.xml b/merchant-terminal/src/main/res/values/ic_taler_pos_logo_background.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ This file is part of GNU Taler + ~ (C) 2026 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<resources> + <color name="ic_taler_pos_logo_background">#FFFFFF</color> +</resources> diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ <resources> - <string name="app_name">GNU Taler Point-of-Sale</string> - <string name="app_name_short">Merchant Terminal</string> + <string name="app_name">Taler POS</string> + <string name="app_name_short">Taler POS</string> <string name="project_name">GNU Taler</string> <string name="menu_order">Order by inventory</string> @@ -14,6 +14,7 @@ <string name="product_category_uncategorized">Uncategorized</string> <string name="product_unavailable">Unavailable</string> <string name="product_out_of_stock">Out of stock</string> + <string name="product_wrong_currency">Wrong currency</string> <string name="order_label_title">Order #%s</string> <!-- The placeholder is the total order amount with currency --> @@ -43,15 +44,15 @@ <string name="settings_language_label">Language</string> <string name="settings_language_hint">App language</string> <string name="settings_language_system_default">System default</string> - <string name="settings_subtitle">Choose the app language and manage merchant instance credentials.</string> + <string name="settings_subtitle">Choose the app language and manage login settings.</string> <string name="settings_app_title">App settings</string> <string name="settings_language_description">Applied immediately to the app interface.</string> <string name="settings_initial_order_label">Initial order screen</string> <string name="settings_initial_order_hint">Default order mode</string> <string name="settings_initial_order_description">Shown when the app starts or after configuration is loaded.</string> - <string name="settings_instance_title">Saved instance</string> - <string name="settings_instance_description">Change merchant portal URL, username, token, and other instance-specific settings.</string> - <string name="settings_instance_button">Modify saved instance</string> + <string name="settings_instance_title">Login settings</string> + <string name="settings_instance_description">Change merchant portal URL, username, password, and other settings.</string> + <string name="settings_instance_button">Modify login settings</string> <string name="settings_logout_button">Log out</string> <string name="config_old_label">JSON file (old)</string> <string name="config_setup_password">Password</string> @@ -61,7 +62,7 @@ <string name="config_username">Username</string> <string name="config_password">Password</string> <string name="config_manual_label">Manual config</string> - <string name="config_token">Access token</string> + <string name="config_token">Password</string> <string name="config_ok">Connect</string> <string name="config_auth_error">Error: Invalid username or password</string> <string name="config_error_network">Error: Could not connect to configuration server</string> @@ -87,6 +88,9 @@ <string name="payment_order_id">Receipt #%s</string> <string name="payment_process_label">Payment required</string> <string name="payment_canceled">Payment canceled</string> + <string name="force_delete_dialog_title">Force delete order?</string> + <string name="force_delete_dialog_message">This order has been claimed by a wallet. Force deleting it will release any inventory locks and prevent the wallet from completing the payment.</string> + <string name="force_delete_dialog_confirm">Force delete</string> <string name="payment_share_uri">Share payment URI</string> <string name="payment_copy_uri">Copy payment URI</string> @@ -137,7 +141,7 @@ <string name="forever">Forever</string> <string name="custom_duration">Custom duration</string> <string name="duration">Duration</string> - <string name="token_validity_deadline">Token validity deadline:</string> + <string name="token_validity_deadline">Password validity deadline:</string> <string name="expires_on">Expires on…</string> <string name="never_expires">Never expires</string> <string name="no_deadline_set">No deadline set</string> diff --git a/merchant-terminal/src/test/java/net/taler/merchantpos/config/ConfigProductTest.kt b/merchant-terminal/src/test/java/net/taler/merchantpos/config/ConfigProductTest.kt @@ -50,4 +50,71 @@ class ConfigProductTest { assertEquals("Fr.2.50", product.displayPrice) } + + @Test + fun `stockLimit subtracts sold and lost from total stock`() { + val product = ConfigProduct( + description = "finite_quantity", + price = Amount("KUDOS", 10, 0), + categories = emptyList(), + totalStock = 10, + unitTotalStock = "10", + totalSold = 0, + totalLost = 2, + ) + + assertEquals(8, product.stockLimit) + } + + @Test + fun `stockLimit subtracts unit sold and lost`() { + val product = ConfigProduct( + description = "test", + price = Amount("KUDOS", 5, 0), + categories = emptyList(), + unitTotalStock = "10", + unitTotalSold = "2", + unitTotalLost = "3", + ) + + assertEquals(5, product.stockLimit) + } + + @Test + fun `stockLimit with no sold or lost returns total stock`() { + val product = ConfigProduct( + description = "test", + price = Amount("KUDOS", 5, 0), + categories = emptyList(), + totalStock = 10, + ) + + assertEquals(10, product.stockLimit) + } + + @Test + fun `stockLimit returns null for unlimited stock`() { + val product = ConfigProduct( + description = "test", + price = Amount("KUDOS", 5, 0), + categories = emptyList(), + totalStock = -1, + ) + + assertNull(product.stockLimit) + } + + @Test + fun `stockLimit clamps to zero when sold and lost exceed stock`() { + val product = ConfigProduct( + description = "test", + price = Amount("KUDOS", 5, 0), + categories = emptyList(), + totalStock = 5, + totalSold = 3, + totalLost = 5, + ) + + assertEquals(0, product.stockLimit) + } } diff --git a/taler-kotlin-android/src/main/java/net/taler/lib/android/MfaUtils.kt b/taler-kotlin-android/src/main/java/net/taler/lib/android/MfaUtils.kt @@ -19,6 +19,7 @@ package net.taler.lib.android import android.content.res.ColorStateList import android.content.DialogInterface import android.text.Editable +import android.text.InputFilter import android.text.TextWatcher import android.view.KeyEvent import android.view.LayoutInflater @@ -227,7 +228,11 @@ private fun setupMfaCodeInputs( errorView: TextView, onSubmit: () -> Boolean, ) { + var updatingInputs = false inputs.forEachIndexed { index, input -> + input.filters = input.filters + .filterNot { it is InputFilter.LengthFilter } + .toTypedArray() input.imeOptions = if (index == inputs.lastIndex) { EditorInfo.IME_ACTION_DONE } else { @@ -238,8 +243,30 @@ private fun setupMfaCodeInputs( override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit override fun afterTextChanged(s: Editable?) { + if (updatingInputs) return errorView.visibility = GONE - if (s?.length == 1 && index < inputs.lastIndex) { + val value = s?.toString().orEmpty() + if (value.length > 1) { + val pastedCode = normalizeMfaCode(value) + updatingInputs = true + try { + if (pastedCode == null) { + input.text?.clear() + } else { + inputs.forEachIndexed { digitIndex, digitInput -> + digitInput.setText(pastedCode[digitIndex].toString()) + } + } + } finally { + updatingInputs = false + } + if (pastedCode != null) { + inputs.last().apply { + requestFocus() + setSelection(text?.length ?: 0) + } + } + } else if (value.length == 1 && index < inputs.lastIndex) { inputs[index + 1].requestFocus() } } @@ -276,6 +303,11 @@ private fun setupMfaCodeInputs( } } +internal fun normalizeMfaCode(value: String): String? { + val digits = value.filter(Char::isDigit) + return digits.takeIf { it.length == 8 } +} + private fun collectMfaCode(inputs: List<EditText>): String? { val digits = inputs.map { it.text?.toString().orEmpty() } if (digits.any { it.length != 1 }) return null diff --git a/taler-kotlin-android/src/test/java/net/taler/lib/android/MfaUtilsTest.kt b/taler-kotlin-android/src/test/java/net/taler/lib/android/MfaUtilsTest.kt @@ -0,0 +1,39 @@ +/* + * This file is part of GNU Taler + * (C) 2026 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.lib.android + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class MfaUtilsTest { + + @Test + fun normalizesEightDigitCodes() { + assertEquals("12345678", normalizeMfaCode("12345678")) + assertEquals("12345678", normalizeMfaCode("1234-5678")) + assertEquals("12345678", normalizeMfaCode(" 1234 5678 ")) + assertEquals("12345678", normalizeMfaCode("12-34 / 56.78")) + } + + @Test + fun rejectsCodesWithoutExactlyEightDigits() { + assertNull(normalizeMfaCode("1234567")) + assertNull(normalizeMfaCode("123456789")) + assertNull(normalizeMfaCode("no code")) + } +}