commit 7b9632146a1cc3bd73b81da8de80e2301f93db97
parent a26f5a6c4b012f625005d9d729a56a7d1d88f984
Author: Bohdan Potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Tue, 19 May 2026 18:21:21 +0200
[pos] adding missing files
Diffstat:
4 files changed, 244 insertions(+), 0 deletions(-)
diff --git a/merchant-terminal/scripts/capture-screenshots.sh b/merchant-terminal/scripts/capture-screenshots.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+OUT_DIR="${1:-$REPO_ROOT/merchant-terminal/build/screenshots}"
+shift || true
+
+if [[ $# -gt 0 ]]; then
+ SCENARIOS=("$@")
+else
+ SCENARIOS=("amount-entry" "order" "payment" "payment-success")
+fi
+
+APP_ID="net.taler.merchantpos"
+ACTIVITY="$APP_ID/.MainActivity"
+EXTRA_KEY="taler_pos_screenshot_scenario"
+
+mkdir -p "$OUT_DIR"
+
+if ! command -v adb >/dev/null 2>&1; then
+ echo "adb is required" >&2
+ exit 1
+fi
+
+if ! adb get-state >/dev/null 2>&1; then
+ echo "No Android device or emulator detected" >&2
+ exit 1
+fi
+
+"$REPO_ROOT/gradlew" :merchant-terminal:installDebug
+
+for scenario in "${SCENARIOS[@]}"; do
+ echo "Capturing $scenario"
+ adb shell am force-stop "$APP_ID" >/dev/null 2>&1 || true
+ adb shell am start -W -n "$ACTIVITY" --es "$EXTRA_KEY" "$scenario" >/dev/null
+ sleep 2
+ adb exec-out screencap -p > "$OUT_DIR/$scenario.png"
+done
+
+echo "Saved screenshots to $OUT_DIR"
diff --git a/merchant-terminal/scripts/generate-screenshot-product-images.sh b/merchant-terminal/scripts/generate-screenshot-product-images.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ASSET_DIR="$SCRIPT_DIR/../src/debug/assets/screenshot-products"
+FONT_PATH="/System/Library/Fonts/Supplemental/Arial Bold.ttf"
+mkdir -p "$ASSET_DIR"
+
+magick -size 640x640 xc:'#f3e8d2' \
+ -fill '#6b3f2c' -draw 'roundrectangle 170,180 470,500 42,42' \
+ -fill '#f8f4ec' -draw 'ellipse 320,250 88,44 0,360' \
+ -fill '#dcc7ae' -draw 'ellipse 320,250 70,30 0,360' \
+ -fill '#6b3f2c' -gravity south -font "$FONT_PATH" -pointsize 54 -annotate +0+48 'Coffee' \
+ "$ASSET_DIR/coffee.png"
+
+magick -size 640x640 xc:'#dff1e2' \
+ -fill '#72a96c' -draw 'polygon 320,150 430,350 210,350' \
+ -fill '#c7e0a8' -draw 'polygon 320,210 390,330 250,330' \
+ -fill '#3c6b48' -gravity south -font "$FONT_PATH" -pointsize 54 -annotate +0+48 'Tea' \
+ "$ASSET_DIR/tea.png"
+
+magick -size 640x640 xc:'#f6e7d7' \
+ -fill '#c8843b' -draw 'path \"M 150,360 Q 320,120 490,360 Q 320,470 150,360 z\"' \
+ -fill '#e3ad62' -draw 'path \"M 210,335 Q 320,190 430,335 Q 320,405 210,335 z\"' \
+ -fill '#80511f' -gravity south -font "$FONT_PATH" -pointsize 54 -annotate +0+48 'Croissant' \
+ "$ASSET_DIR/croissant.png"
+
+magick -size 640x640 xc:'#efe7dc' \
+ -fill '#c9914e' -draw 'roundrectangle 140,210 500,450 36,36' \
+ -fill '#7ebc6f' -draw 'roundrectangle 165,250 475,300 25,25' \
+ -fill '#d94b46' -draw 'roundrectangle 165,305 475,340 20,20' \
+ -fill '#f4deb2' -draw 'roundrectangle 165,345 475,410 24,24' \
+ -fill '#70491e' -gravity south -font "$FONT_PATH" -pointsize 50 -annotate +0+48 'Sandwich' \
+ "$ASSET_DIR/sandwich.png"
diff --git a/merchant-terminal/src/debug/java/net/taler/merchantpos/debug/ScreenshotController.kt b/merchant-terminal/src/debug/java/net/taler/merchantpos/debug/ScreenshotController.kt
@@ -0,0 +1,155 @@
+package net.taler.merchantpos.debug
+
+import android.content.Context
+import android.content.Intent
+import android.util.Base64
+import net.taler.common.Amount
+import net.taler.merchantlib.MerchantConfig
+import net.taler.merchantpos.MainViewModel
+import net.taler.merchantpos.PosDestination
+import net.taler.merchantpos.config.Category
+import net.taler.merchantpos.config.ConfigProduct
+import net.taler.merchantpos.config.InitialOrderScreen
+import net.taler.merchantpos.config.PosConfig
+import net.taler.merchantpos.order.Order
+
+object ScreenshotController {
+ private const val EXTRA_SCENARIO = "taler_pos_screenshot_scenario"
+ private const val FIXTURE_ASSET_DIR = "screenshot-products"
+ private const val CURRENCY = "CHF"
+
+ @Volatile
+ private var activeScenario: Scenario? = null
+
+ val isActive: Boolean
+ get() = activeScenario != null
+
+ fun prepareScenario(intent: Intent, model: MainViewModel): PosDestination? {
+ val scenario = intent.getStringExtra(EXTRA_SCENARIO)
+ ?.let(Scenario::fromValue)
+ ?: run {
+ activeScenario = null
+ return null
+ }
+ activeScenario = scenario
+ applyScenario(model, scenario)
+ return scenario.destination
+ }
+
+ private fun applyScenario(model: MainViewModel, scenario: Scenario) {
+ val fixture = buildFixture(model.getApplication())
+ model.configManager.debugApplyFixture(
+ posConfig = fixture.posConfig,
+ merchantConfig = fixture.merchantConfig,
+ currency = CURRENCY,
+ initialOrderScreen = InitialOrderScreen.Inventory,
+ )
+ model.orderManager.debugSeedCurrentOrder(
+ listOf("coffee", "croissant", "sandwich", "coffee"),
+ )
+ val currentOrderId = model.orderManager.currentOrderId.value ?: 0
+ val currentOrder = model.orderManager.getOrder(currentOrderId).order.value
+ ?: Order(
+ id = currentOrderId,
+ currency = CURRENCY,
+ currencySpec = null,
+ availableCategories = emptyMap(),
+ )
+
+ when (scenario) {
+ Scenario.AmountEntry -> Unit
+ Scenario.Order -> Unit
+ Scenario.Payment -> {
+ model.paymentManager.debugSetPayment(
+ net.taler.merchantpos.payment.Payment(
+ order = currentOrder,
+ summary = currentOrder.summary,
+ currency = CURRENCY,
+ orderId = "2026-ORD-1042",
+ talerPayUri = "taler://pay/example.invalid/2026-ORD-1042/ZXCVBNM123456",
+ ),
+ )
+ }
+ Scenario.PaymentSuccess -> Unit
+ }
+ }
+
+ private fun buildFixture(context: Context): FixtureData {
+ val categories = listOf(
+ Category(id = 1, name = "Drinks"),
+ Category(id = 2, name = "Bakery"),
+ Category(id = 3, name = "Lunch"),
+ )
+ val products = listOf(
+ ConfigProduct(
+ id = "coffee",
+ productId = "coffee",
+ productName = "House Coffee",
+ description = "House Coffee",
+ price = Amount.fromString(CURRENCY, "3.80"),
+ categories = listOf(1),
+ image = assetDataUri(context, "coffee.png"),
+ ),
+ ConfigProduct(
+ id = "tea",
+ productId = "tea",
+ productName = "Herbal Tea",
+ description = "Herbal Tea",
+ price = Amount.fromString(CURRENCY, "3.40"),
+ categories = listOf(1),
+ image = assetDataUri(context, "tea.png"),
+ ),
+ ConfigProduct(
+ id = "croissant",
+ productId = "croissant",
+ productName = "Butter Croissant",
+ description = "Butter Croissant",
+ price = Amount.fromString(CURRENCY, "2.90"),
+ categories = listOf(2),
+ image = assetDataUri(context, "croissant.png"),
+ ),
+ ConfigProduct(
+ id = "sandwich",
+ productId = "sandwich",
+ productName = "Veggie Sandwich",
+ description = "Veggie Sandwich",
+ price = Amount.fromString(CURRENCY, "8.50"),
+ categories = listOf(3),
+ image = assetDataUri(context, "sandwich.png"),
+ ),
+ )
+ return FixtureData(
+ posConfig = PosConfig(categories = categories, products = products),
+ merchantConfig = MerchantConfig(
+ baseUrl = "https://merchant.example.invalid/instances/demo",
+ apiKey = "",
+ ),
+ )
+ }
+
+ private fun assetDataUri(context: Context, fileName: String): String {
+ val bytes = context.assets.open("$FIXTURE_ASSET_DIR/$fileName").use { it.readBytes() }
+ val encoded = Base64.encodeToString(bytes, Base64.NO_WRAP)
+ return "data:image/png;base64,$encoded"
+ }
+
+ private data class FixtureData(
+ val posConfig: PosConfig,
+ val merchantConfig: MerchantConfig,
+ )
+
+ private enum class Scenario(
+ val value: String,
+ val destination: PosDestination,
+ ) {
+ AmountEntry("amount-entry", PosDestination.AmountEntry),
+ Order("order", PosDestination.Order),
+ Payment("payment", PosDestination.ProcessPayment),
+ PaymentSuccess("payment-success", PosDestination.PaymentSuccess),
+ ;
+
+ companion object {
+ fun fromValue(value: String): Scenario? = entries.firstOrNull { it.value == value }
+ }
+ }
+}
diff --git a/merchant-terminal/src/release/java/net/taler/merchantpos/debug/ScreenshotController.kt b/merchant-terminal/src/release/java/net/taler/merchantpos/debug/ScreenshotController.kt
@@ -0,0 +1,12 @@
+package net.taler.merchantpos.debug
+
+import android.content.Intent
+import net.taler.merchantpos.MainViewModel
+import net.taler.merchantpos.PosDestination
+
+object ScreenshotController {
+ val isActive: Boolean
+ get() = false
+
+ fun prepareScenario(intent: Intent, model: MainViewModel): PosDestination? = null
+}