taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

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:
Amerchant-terminal/scripts/capture-screenshots.sh | 42++++++++++++++++++++++++++++++++++++++++++
Amerchant-terminal/scripts/generate-screenshot-product-images.sh | 35+++++++++++++++++++++++++++++++++++
Amerchant-terminal/src/debug/java/net/taler/merchantpos/debug/ScreenshotController.kt | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amerchant-terminal/src/release/java/net/taler/merchantpos/debug/ScreenshotController.kt | 12++++++++++++
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 +}