commit a26f5a6c4b012f625005d9d729a56a7d1d88f984
parent 5bf1f8c6655e5a39d3202edd32365097e5d043cf
Author: Bohdan Potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Tue, 19 May 2026 14:36:52 +0200
[pos] bug fixes
Diffstat:
8 files changed, 133 insertions(+), 7 deletions(-)
diff --git a/merchant-terminal/SCREENSHOTS.md b/merchant-terminal/SCREENSHOTS.md
@@ -0,0 +1,74 @@
+# Merchant Terminal screenshots
+
+This module now has a debug-only screenshot mode for deterministic captures of real app screens.
+
+## What it does
+
+- Launches the app directly into a named scenario using an intent extra.
+- Seeds fixed merchant config, products, totals, and product thumbnails in-process.
+- Captures the resulting screen with `adb exec-out screencap`.
+
+Bundled fixture product images are generated locally by `merchant-terminal/scripts/generate-screenshot-product-images.sh`, so the default workflow does not depend on third-party image licenses.
+
+## Supported scenarios
+
+- `amount-entry`
+- `order`
+- `payment`
+- `payment-success`
+
+## Generate fixture product images
+
+```bash
+merchant-terminal/scripts/generate-screenshot-product-images.sh
+```
+
+The images are written to `merchant-terminal/src/debug/assets/screenshot-products/`.
+
+## Capture screenshots
+
+Connect an emulator or device, then run:
+
+```bash
+merchant-terminal/scripts/capture-screenshots.sh
+```
+
+Custom output directory:
+
+```bash
+merchant-terminal/scripts/capture-screenshots.sh /tmp/merchant-terminal-shots
+```
+
+Specific scenarios only:
+
+```bash
+merchant-terminal/scripts/capture-screenshots.sh /tmp/merchant-terminal-shots order payment
+```
+
+## Intent hook
+
+The debug hook is activated with:
+
+```text
+--es taler_pos_screenshot_scenario <scenario>
+```
+
+Example:
+
+```bash
+adb shell am start -W \
+ -n net.taler.merchantpos/.MainActivity \
+ --es taler_pos_screenshot_scenario order
+```
+
+## If you want real photos later
+
+For external product photos, use sources with clear reuse terms and record the exact asset URLs and license notes alongside the files you import.
+
+Reasonable options:
+
+- Pexels license: https://www.pexels.com/license/
+- Unsplash license: https://unsplash.com/license
+- Wikimedia Commons reuse guide: https://commons.wikimedia.org/wiki/Commons:Simple_media_reuse_guide
+
+For maximum simplicity in a repo, prefer public-domain or CC0 assets, or keep using locally generated fixture images for screenshot automation.
diff --git a/merchant-terminal/build.gradle b/merchant-terminal/build.gradle
@@ -13,8 +13,8 @@ android {
applicationId "net.taler.merchantpos"
minSdkVersion 23
targetSdkVersion 36
- versionCode 21
- versionName "1.5.0"
+ versionCode 22
+ versionName "1.5.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "BACKEND_API_VERSION", "\"20:0:8\"")
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainActivity.kt
@@ -87,6 +87,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.launch
import net.taler.lib.android.TalerNfcService
+import net.taler.merchantpos.debug.ScreenshotController
import net.taler.merchantpos.compose.PosTheme
import net.taler.merchantpos.config.Config
import net.taler.merchantpos.config.ConfigFetcherFragment
@@ -118,6 +119,8 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val screenshotStartDestination = ScreenshotController.prepareScenario(intent, model)
+
TalerNfcService.startService(this)
model.paymentManager.payment.observe(this) { payment ->
@@ -137,7 +140,7 @@ class MainActivity : AppCompatActivity() {
PosTheme {
MerchantTerminalApp(
viewModel = model,
- startDestination = determineStartDestination(),
+ startDestination = determineStartDestination(screenshotStartDestination),
onNavControllerReady = { navController = it },
onExitRequested = ::handleExitRequest,
)
@@ -258,7 +261,8 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun determineStartDestination(): PosDestination {
+ private fun determineStartDestination(overrideDestination: PosDestination? = null): PosDestination {
+ overrideDestination?.let { return it }
return when {
!model.configManager.config.isValid() -> PosDestination.Config
model.configManager.merchantConfig == null || model.configManager.currency == null ->
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
@@ -42,6 +42,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
@@ -235,6 +236,37 @@ class ConfigManager(
}
}
+ internal fun debugApplyFixture(
+ posConfig: PosConfig,
+ merchantConfig: MerchantConfig,
+ currency: String,
+ currencySpec: CurrencySpecification? = null,
+ initialOrderScreen: InitialOrderScreen = InitialOrderScreen.Inventory,
+ ) {
+ config = Config.New(
+ merchantUrl = merchantConfig.baseUrl,
+ accessToken = "",
+ savePassword = false,
+ )
+ this.merchantConfig = merchantConfig
+ this.currency = currency
+ this.currencySpec = currencySpec
+ this.initialOrderScreen = initialOrderScreen
+
+ val snapshot = CachedRuntimeConfig(
+ posConfig = posConfig,
+ merchantConfig = merchantConfig,
+ currency = currency,
+ currencySpec = currencySpec,
+ )
+ saveCachedRuntimeConfig(snapshot)
+ runBlocking {
+ configurationReceivers.forEach { receiver ->
+ receiver.onConfigurationReceived(posConfig, currency, currencySpec)
+ }
+ }
+ }
+
private fun migrateLegacyPrefsIfNeeded() {
val legacyVersion = prefs.getInt(SETTINGS_CONFIG_VERSION, CONFIG_VERSION_NEW)
if (legacyVersion == CONFIG_VERSION_OLD) {
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/GeneralSettingsFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/GeneralSettingsFragment.kt
@@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -190,7 +189,6 @@ private fun GeneralSettingsScreen(
LazyColumn(
modifier = Modifier
.fillMaxSize()
- .statusBarsPadding()
.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(14.dp),
) {
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
@@ -243,6 +243,16 @@ class OrderManager(private val context: Context) : ConfigurationReceiver {
}
@UiThread
+ internal fun debugSeedCurrentOrder(productIds: List<String>) {
+ val orderId = currentOrderId.value ?: return
+ val liveOrder = order(orderId)
+ productIds.forEach { productId ->
+ productsById[productId]?.let(liveOrder::addProduct)
+ }
+ updateVisibleProducts()
+ }
+
+ @UiThread
internal fun onOrderPaid(orderId: Int) {
if (currentOrderId.value == orderId) {
if (hasPreviousOrder(orderId)) previousOrder()
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
@@ -147,6 +147,13 @@ class PaymentManager(
}
}
+ @UiThread
+ internal fun debugSetPayment(payment: Payment) {
+ checkTimer.cancel()
+ checkJob = null
+ mPayment.value = payment
+ }
+
private fun checkPayment(orderId: String) = scope.launch {
val merchantConfig = configManager.merchantConfig!!
api.checkOrder(merchantConfig, orderId).handle({ error ->
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
@@ -258,9 +258,10 @@ private fun TabletProcessPaymentScreen(
textAlign = TextAlign.Center,
)
}
+ Spacer(modifier = Modifier.weight(1f))
OutlinedButton(
onClick = onCancel,
- modifier = Modifier.align(Alignment.Start),
+ modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError,