From daccafe52ca3e49565ee41b52edbada8d6b3781f Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 13:14:13 +0200 Subject: [PATCH 01/10] Simplify question/dialog calls --- .../pretixscan/droid/ui/BaseScanActivity.kt | 7 ++---- .../pretixscan/droid/ui/MainActivity.kt | 24 +++++++++---------- .../droid/ui/UnpaidOrderDialogHelper.kt | 22 +++++------------ 3 files changed, 20 insertions(+), 33 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index 58d51428..c721998b 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -585,12 +585,9 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann open fun showLoadingCard() {} fun showQuestionsDialog(res: TicketCheckProvider.CheckResult, - secret: String, - sourceType: ReusableMediaType, - ignore_unpaid: Boolean, values: Map?, isResumed: Boolean, - retryHandler: ((String, ReusableMediaType, MutableList, Boolean) -> Unit)): QuestionsDialogInterface { + retryHandler: ((MutableList) -> Unit)): QuestionsDialogInterface { val questions = res.requiredAnswers!!.map { it.question.toModel() } for (q in questions) { q.resolveDependency(questions) @@ -620,7 +617,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann values_, null, null, - { answers -> retryHandler(secret, sourceType, answers, ignore_unpaid) }, + retryHandler, null, attendeeName, res.orderCodeAndPositionId(), diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 0105c933..f69dadb4 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -652,10 +652,10 @@ class MainActivity : BaseScanActivity() { stopHidingTimer() if (result.type == TicketCheckProvider.CheckResult.Type.ANSWERS_REQUIRED) { view_data.resultState.set(DIALOG_QUESTIONS) - dialog = showQuestionsDialog(result, lastScanCode, lastScanSourceType, ignore_unpaid, null, false) { secret, sourceType, answers, ignore_unpaid -> + dialog = showQuestionsDialog(result, null, false) { answers -> handleScan( - secret, - sourceType.serverName!!, + lastScanCode, + lastScanSourceType.serverName!!, answers, ignore_unpaid ) @@ -683,16 +683,16 @@ class MainActivity : BaseScanActivity() { } if (result.type == TicketCheckProvider.CheckResult.Type.UNPAID && result.isCheckinAllowed) { view_data.resultState.set(DIALOG_QUESTIONS) - dialog = showUnpaidDialog(this, result, lastScanCode, lastScanSourceType, answers) { secret, sourceType, answers, ignore_unpaid -> + dialog = showUnpaidDialog(this) { stopHidingTimer() handleScan( - secret, - sourceType.serverName!!, + lastScanCode, + lastScanSourceType.serverName!!, answers, - ignore_unpaid + true ) } - dialog!!.setOnCancelListener(DialogInterface.OnCancelListener { hideCard() }) + dialog!!.setOnCancelListener { hideCard() } view_data.setLed(this, view_data.resultState.get()!!, true) return } @@ -1014,13 +1014,13 @@ class MainActivity : BaseScanActivity() { } } - dialog = showQuestionsDialog(lastScanResult!!, lastScanCode, lastScanSourceType, lastIgnoreUnpaid, values, true) { secret, sourceType, answers, ignore_unpaid -> + dialog = showQuestionsDialog(lastScanResult!!, values, true) { answers -> stopHidingTimer() handleScan( - secret, - sourceType.serverName!!, + lastScanCode, + lastScanSourceType.serverName!!, answers, - ignore_unpaid + lastIgnoreUnpaid ) } dialog!!.onRestoreInstanceState(answers) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/UnpaidOrderDialogHelper.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/UnpaidOrderDialogHelper.kt index ba6d19e9..783fff60 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/UnpaidOrderDialogHelper.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/UnpaidOrderDialogHelper.kt @@ -1,27 +1,22 @@ package eu.pretix.pretixscan.droid.ui import android.app.Activity -import android.content.DialogInterface import android.content.Intent import android.view.WindowManager import androidx.appcompat.app.AlertDialog -import eu.pretix.libpretixsync.check.TicketCheckProvider -import eu.pretix.libpretixsync.db.Answer -import eu.pretix.libpretixsync.db.ReusableMediaType import eu.pretix.libpretixui.android.questions.QuestionsDialogInterface import eu.pretix.pretixscan.droid.R -class UnpaidDialog(ctx: Activity, val secret: String, val sourceType: ReusableMediaType, val answers: MutableList?, - val retryHandler: ((String, ReusableMediaType, MutableList?, Boolean) -> Unit)) : AlertDialog(ctx), QuestionsDialogInterface { +class UnpaidDialog(ctx: Activity, val retryHandler: (() -> Unit)) : AlertDialog(ctx), QuestionsDialogInterface { init { setTitle(R.string.dialog_unpaid_title) setMessage(ctx.getString(R.string.dialog_unpaid_text)) - setButton(DialogInterface.BUTTON_POSITIVE, ctx.getString(R.string.dialog_unpaid_retry)) { p0, p1 -> + setButton(BUTTON_POSITIVE, ctx.getString(R.string.dialog_unpaid_retry)) { _, _ -> dismiss() - retryHandler(secret, sourceType, answers, true) + retryHandler() } - setButton(DialogInterface.BUTTON_NEGATIVE, ctx.getString(eu.pretix.libpretixui.android.R.string.cancel)) { p0, p1 -> + setButton(BUTTON_NEGATIVE, ctx.getString(eu.pretix.libpretixui.android.R.string.cancel)) { _, _ -> cancel() } window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) @@ -32,13 +27,8 @@ class UnpaidDialog(ctx: Activity, val secret: String, val sourceType: ReusableMe } } -fun showUnpaidDialog(ctx: Activity, - res: TicketCheckProvider.CheckResult, - secret: String, - sourceType: ReusableMediaType, - answers: MutableList?, - retryHandler: ((String, ReusableMediaType, MutableList?, Boolean) -> Unit)): QuestionsDialogInterface { - val dialog = UnpaidDialog(ctx, secret, sourceType, answers, retryHandler) +fun showUnpaidDialog(ctx: Activity, retryHandler: (() -> Unit)): QuestionsDialogInterface { + val dialog = UnpaidDialog(ctx, retryHandler) dialog.show() return dialog } From 1efd877c3345a2c5f6352fdc30e01e6424396304 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 13:17:47 +0200 Subject: [PATCH 02/10] Resume unpaid dialog after rotation --- .../pretixscan/droid/ui/BaseScanActivity.kt | 1 + .../pretixscan/droid/ui/MainActivity.kt | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index c721998b..bbbe6840 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -74,6 +74,7 @@ enum class ResultState { LOADING, ERROR, DIALOG_QUESTIONS, + DIALOG_UNPAID, DIALOG_EXCHANGE, WARNING, SUCCESS, diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index f69dadb4..70237299 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -94,7 +94,7 @@ class ViewDataHolder(private val ctx: Context) { fun getColor(state: ResultState): Int { return ctx.resources.getColor(when (state) { - EMPTY, DIALOG_QUESTIONS, DIALOG_EXCHANGE, LOADING -> R.color.pretix_brand_lightgrey + EMPTY, DIALOG_QUESTIONS, DIALOG_UNPAID, DIALOG_EXCHANGE, LOADING -> R.color.pretix_brand_lightgrey ERROR -> R.color.pretix_brand_red WARNING -> R.color.pretix_brand_orange SUCCESS, SUCCESS_EXIT -> R.color.pretix_brand_green @@ -106,7 +106,7 @@ class ViewDataHolder(private val ctx: Context) { when (state) { EMPTY -> led.off() LOADING -> led.progress() - DIALOG_QUESTIONS, DIALOG_EXCHANGE, WARNING -> led.attention(blink = needsAttention) + DIALOG_QUESTIONS, DIALOG_UNPAID, DIALOG_EXCHANGE, WARNING -> led.attention(blink = needsAttention) ERROR -> led.error() SUCCESS, SUCCESS_EXIT -> led.success(blink = needsAttention) } @@ -682,7 +682,7 @@ class MainActivity : BaseScanActivity() { return } if (result.type == TicketCheckProvider.CheckResult.Type.UNPAID && result.isCheckinAllowed) { - view_data.resultState.set(DIALOG_QUESTIONS) + view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { stopHidingTimer() handleScan( @@ -1025,6 +1025,18 @@ class MainActivity : BaseScanActivity() { } dialog!!.onRestoreInstanceState(answers) dialog!!.setOnCancelListener { hideCard() } + } else if (resultState == "DIALOG_UNPAID") { + view_data.resultState.set(DIALOG_UNPAID) + dialog = showUnpaidDialog(this) { + hideCard() + handleScan( + lastScanCode, + lastScanSourceType.serverName!!, + null, + true + ) + } + dialog!!.setOnCancelListener { hideCard() } } else if (resultState == "DIALOG_EXCHANGE") { view_data.resultState.set(DIALOG_EXCHANGE) reloadNfcHandler() // else nfchandler is null @@ -1053,7 +1065,7 @@ class MainActivity : BaseScanActivity() { // if the questions dialog starts sub-activities, e.g. for taking photos. In these case, // we try to serialize all state required to re-create the dialog if the user returns. - if (view_data.resultState.get() in listOf(DIALOG_QUESTIONS, DIALOG_EXCHANGE) && dialog != null && lastScanResult != null) { + if (view_data.resultState.get() in listOf(DIALOG_QUESTIONS, DIALOG_EXCHANGE, DIALOG_UNPAID) && dialog != null && lastScanResult != null) { val module = SimpleModule() module.addSerializer(JSONObject::class.java, JSONObjectSerializer()) module.addSerializer(JSONArray::class.java, JSONArraySerializer()) From 106cb9d0f875d78ca3c5006d2c70034cea18db5d Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 14:08:45 +0200 Subject: [PATCH 03/10] Refactor handleScan: use ReusableMediaType --- .../pretixscan/droid/ui/BaseScanActivity.kt | 16 ++++++------- .../pretixscan/droid/ui/KioskActivity.kt | 7 +++--- .../pretixscan/droid/ui/MainActivity.kt | 24 +++++++++---------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index bbbe6840..fe087da8 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -122,7 +122,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann lastScanResult = null handleScan( result, - lastScanSourceType.serverName!!, + lastScanSourceType, null, !conf.unpaidAsk ) @@ -493,10 +493,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann open fun handleScan( raw_result: String, - source_type: String, + source_type: ReusableMediaType, answers: MutableList?, ignore_unpaid: Boolean = false, - exchange_medium_type: String? = null, + exchange_medium_type: ReusableMediaType? = null, exchange_medium_identifier: String? = null, ) { if (dialog?.isShowing() == true) { @@ -543,7 +543,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann checkResult = provider.check( conf.eventSelectionToMap(), result, - source_type, + source_type.serverName!!, answers, ignore_unpaid, conf.printBadges, @@ -553,7 +553,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann }, allowQuestions = !conf.ignoreQuestions, useOrderLocale = useOrderLocale, - exchange_medium_type = exchange_medium_type, + exchange_medium_type = exchange_medium_type?.serverName, exchange_medium_identifier = exchange_medium_identifier, ) if (provider is OnlineCheckProvider) { @@ -648,7 +648,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann lastIgnoreUnpaid = false handleScan( s, - lastScanSourceType.serverName!!, + lastScanSourceType, null, !conf.unpaidAsk ) @@ -670,7 +670,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann lastIgnoreUnpaid = false handleScan( keyboardBuffer, - lastScanSourceType.serverName!!, + lastScanSourceType, null, !conf.unpaidAsk ) @@ -774,7 +774,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann handleScan( identifier, - mediaType.serverName!!, + mediaType, null, !conf.unpaidAsk ) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt index e42643b3..0c55cff8 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -39,6 +39,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import eu.pretix.libpretixsync.api.PretixApi import eu.pretix.libpretixsync.check.TicketCheckProvider import eu.pretix.libpretixsync.db.Answer +import eu.pretix.libpretixsync.db.ReusableMediaType import eu.pretix.pretixscan.droid.AndroidHttpClientFactory import eu.pretix.pretixscan.droid.BuildConfig import eu.pretix.pretixscan.droid.PretixScan @@ -829,10 +830,10 @@ class KioskActivity : BaseScanActivity() { override fun handleScan( raw_result: String, - source_type: String, + source_type: ReusableMediaType, answers: MutableList?, ignore_unpaid: Boolean, - exchange_medium_type: String?, + exchange_medium_type: ReusableMediaType?, exchange_medium_identifier: String? ) { if (conf.requiresPin("settings") && conf.verifyPin(raw_result)) { @@ -872,7 +873,7 @@ class KioskActivity : BaseScanActivity() { updateUi() super.handleScan( raw_result, - lastScanSourceType.serverName!!, + source_type, answers, ignore_unpaid, exchange_medium_type, diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 70237299..b4884c0a 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -211,7 +211,7 @@ class MainActivity : BaseScanActivity() { hideSearchCard() handleScan( res.secret!!, - lastScanSourceType.serverName!!, + ReusableMediaType.BARCODE, null, !conf.unpaidAsk ) @@ -584,10 +584,10 @@ class MainActivity : BaseScanActivity() { override fun handleScan( raw_result: String, - source_type: String, + source_type: ReusableMediaType, answers: MutableList?, ignore_unpaid: Boolean, - exchange_medium_type: String?, + exchange_medium_type: ReusableMediaType?, exchange_medium_identifier: String?, ) { if (dialog?.isShowing() == true) { @@ -607,7 +607,7 @@ class MainActivity : BaseScanActivity() { hideSearchCard() super.handleScan( raw_result, - lastScanSourceType.serverName!!, + lastScanSourceType, answers, ignore_unpaid, exchange_medium_type, @@ -655,7 +655,7 @@ class MainActivity : BaseScanActivity() { dialog = showQuestionsDialog(result, null, false) { answers -> handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, answers, ignore_unpaid ) @@ -670,10 +670,10 @@ class MainActivity : BaseScanActivity() { hideCard() handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, null, ignore_unpaid, - exchange_medium_type = mediaType.serverName!!, + exchange_medium_type = mediaType, exchange_medium_identifier = mediaIdentifier, ) } @@ -687,7 +687,7 @@ class MainActivity : BaseScanActivity() { stopHidingTimer() handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, answers, true ) @@ -1018,7 +1018,7 @@ class MainActivity : BaseScanActivity() { stopHidingTimer() handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, answers, lastIgnoreUnpaid ) @@ -1031,7 +1031,7 @@ class MainActivity : BaseScanActivity() { hideCard() handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, null, true ) @@ -1044,10 +1044,10 @@ class MainActivity : BaseScanActivity() { hideCard() handleScan( lastScanCode, - lastScanSourceType.serverName!!, + lastScanSourceType, null, lastIgnoreUnpaid, - exchange_medium_type = mediaType.serverName!!, + exchange_medium_type = mediaType, exchange_medium_identifier = mediaIdentifier, ) } From ad2fb17c770c6f6373cd5e4f262444cbc5b9353b Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 14:55:28 +0200 Subject: [PATCH 04/10] Refactor handleScan: split checkin part, let actions that are not a (maybe) new ticket perform checkin directly --- .../pretix/pretixscan/droid/ui/BaseScanActivity.kt | 11 +++++++++++ .../eu/pretix/pretixscan/droid/ui/MainActivity.kt | 12 ++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index fe087da8..932678c1 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -535,6 +535,17 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann mediaPlayers[R.raw.beep]?.start() } + performCheckin(result, source_type, answers, ignore_unpaid, exchange_medium_type, exchange_medium_identifier) + } + + fun performCheckin( + result: String, + source_type: ReusableMediaType, + answers: MutableList?, + ignore_unpaid: Boolean, + exchange_medium_type: ReusableMediaType? = null, + exchange_medium_identifier: String? = null + ) { bgScope.launch { var checkResult: TicketCheckProvider.CheckResult? = null val provider = (application as PretixScan).getCheckProvider(conf) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index b4884c0a..7c1f9e09 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -653,7 +653,7 @@ class MainActivity : BaseScanActivity() { if (result.type == TicketCheckProvider.CheckResult.Type.ANSWERS_REQUIRED) { view_data.resultState.set(DIALOG_QUESTIONS) dialog = showQuestionsDialog(result, null, false) { answers -> - handleScan( + performCheckin( lastScanCode, lastScanSourceType, answers, @@ -668,7 +668,7 @@ class MainActivity : BaseScanActivity() { view_data.resultState.set(DIALOG_EXCHANGE) dialog = showExchangeDialog(this, result, nfcHandler?.getState()) { mediaIdentifier, mediaType -> hideCard() - handleScan( + performCheckin( lastScanCode, lastScanSourceType, null, @@ -685,7 +685,7 @@ class MainActivity : BaseScanActivity() { view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { stopHidingTimer() - handleScan( + performCheckin( lastScanCode, lastScanSourceType, answers, @@ -1016,7 +1016,7 @@ class MainActivity : BaseScanActivity() { dialog = showQuestionsDialog(lastScanResult!!, values, true) { answers -> stopHidingTimer() - handleScan( + performCheckin( lastScanCode, lastScanSourceType, answers, @@ -1029,7 +1029,7 @@ class MainActivity : BaseScanActivity() { view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { hideCard() - handleScan( + performCheckin( lastScanCode, lastScanSourceType, null, @@ -1042,7 +1042,7 @@ class MainActivity : BaseScanActivity() { reloadNfcHandler() // else nfchandler is null dialog = showExchangeDialog(this, lastScanResult!!, nfcHandler?.getState()) { mediaIdentifier, mediaType -> hideCard() - handleScan( + performCheckin( lastScanCode, lastScanSourceType, null, From e063eec3e6930a472e14911c2a1462193de58c31 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 15:06:55 +0200 Subject: [PATCH 05/10] Refactor handleScan: move persistence into handleScan itself --- .../pretixscan/droid/ui/BaseScanActivity.kt | 67 +++++++------------ .../pretixscan/droid/ui/KioskActivity.kt | 2 - .../pretixscan/droid/ui/MainActivity.kt | 8 +-- 3 files changed, 25 insertions(+), 52 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index 932678c1..4ca3be90 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -115,16 +115,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann if (result == lastScanCode && System.currentTimeMillis() - lastScanTime < 2500) { return } - lastScanTime = System.currentTimeMillis() - lastScanCode = result - lastScanSourceType = ReusableMediaType.BARCODE - lastIgnoreUnpaid = false - lastScanResult = null handleScan( result, - lastScanSourceType, + ReusableMediaType.BARCODE, null, - !conf.unpaidAsk ) } }) @@ -495,7 +489,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann raw_result: String, source_type: ReusableMediaType, answers: MutableList?, - ignore_unpaid: Boolean = false, + ignore_unpaid: Boolean = !conf.unpaidAsk, exchange_medium_type: ReusableMediaType? = null, exchange_medium_identifier: String? = null, ) { @@ -509,20 +503,20 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann val result = if (Regex("^HC1:[0-9A-Z $%*+-./:]+$").matches(raw_result.uppercase(Locale.getDefault()))) { /* - * This is a bit of a hack. pretixSCAN 1.11-2.8.2 supports checking digital COVID vaccination - * certificates. When scanning them at the correct time, we have a high level of privacy - * since we do not store any personal data contained in the certificate. However, if you - * accidentally scan the certificate when you are supposed to scan a ticket, our fancy - * error log will cause the verbatim vaccination certificate to be stored on the server. - * Not really our fault, but also not really nice to store that sensitive health info. - * However, it's still helpful for debugging to see how often an invalid code was scanned. - * So if we encounter something that looks like an EU DGC, we'll just transform it into - * a hashed version. - * - * This hack is safe for pretix' default signature schemes, as they would never generate - * a QR code starting with ``HC1:``, but it could theoretically be unsafe for third-party - * plugins. - */ + * This is a bit of a hack. pretixSCAN 1.11-2.8.2 supported checking digital COVID vaccination + * certificates. When scanning them at the correct time, we have a high level of privacy + * since we do not store any personal data contained in the certificate. However, if you + * accidentally scan the certificate when you are supposed to scan a ticket, our fancy + * error log will cause the verbatim vaccination certificate to be stored on the server. + * Not really our fault, but also not really nice to store that sensitive health info. + * However, it's still helpful for debugging to see how often an invalid code was scanned. + * So if we encounter something that looks like an EU DGC, we'll just transform it into + * a hashed version. + * + * This hack is safe for pretix' default signature schemes, as they would never generate + * a QR code starting with ``HC1:``, but it could theoretically be unsafe for third-party + * plugins. + */ val md = MessageDigest.getInstance("SHA-256") md.update(raw_result.toByteArray(Charset.defaultCharset())) "HC1:hashed:" + Base64.encodeToString(md.digest(), Base64.URL_SAFE) @@ -535,6 +529,12 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann mediaPlayers[R.raw.beep]?.start() } + lastScanTime = System.currentTimeMillis() + lastScanCode = result + lastScanSourceType = source_type + lastIgnoreUnpaid = ignore_unpaid + lastScanResult = null + performCheckin(result, source_type, answers, ignore_unpaid, exchange_medium_type, exchange_medium_identifier) } @@ -652,16 +652,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann if (s == lastScanCode && System.currentTimeMillis() - lastScanTime < 5000) { return } - lastScanTime = System.currentTimeMillis() - lastScanCode = s - lastScanSourceType = ReusableMediaType.BARCODE - lastScanResult = null - lastIgnoreUnpaid = false handleScan( s, - lastScanSourceType, + ReusableMediaType.BARCODE, null, - !conf.unpaidAsk ) } @@ -674,16 +668,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann if (keyboardBuffer.isEmpty()) { return false } - lastScanTime = System.currentTimeMillis() - lastScanCode = keyboardBuffer - lastScanSourceType = ReusableMediaType.BARCODE - lastScanResult = null - lastIgnoreUnpaid = false handleScan( keyboardBuffer, - lastScanSourceType, + ReusableMediaType.BARCODE, null, - !conf.unpaidAsk ) keyboardBuffer = "" true @@ -777,17 +765,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann return } - lastScanTime = System.currentTimeMillis() - lastScanCode = identifier - lastScanSourceType = mediaType - lastScanResult = null - lastIgnoreUnpaid = false - handleScan( identifier, mediaType, null, - !conf.unpaidAsk ) } diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt index 0c55cff8..83d6f66f 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -855,14 +855,12 @@ class KioskActivity : BaseScanActivity() { KioskState.TemporarilyOutOfOrder, KioskState.OutOfOrder, -> { // waiting for user, for printer, gate or administrative action. ignoring scan. - lastScanCode = "" // do not consider scan "used" return } } // ignore scan if same ticket was scanned two times, but gate is already open if (raw_result == lastScanCode && state == KioskState.GateOpen) { - lastScanCode = "" // do not consider scan "used" return } diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 7c1f9e09..aff5ed51 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -203,17 +203,11 @@ class MainActivity : BaseScanActivity() { } searchAdapter = SearchListAdapter(sr, object : SearchResultClickedInterface { override fun onSearchResultClicked(res: TicketCheckProvider.SearchResult) { - lastScanTime = System.currentTimeMillis() - lastScanCode = res.secret!! - lastScanSourceType = ReusableMediaType.BARCODE - lastScanResult = null - lastIgnoreUnpaid = false hideSearchCard() handleScan( res.secret!!, ReusableMediaType.BARCODE, null, - !conf.unpaidAsk ) } }) @@ -607,7 +601,7 @@ class MainActivity : BaseScanActivity() { hideSearchCard() super.handleScan( raw_result, - lastScanSourceType, + source_type, answers, ignore_unpaid, exchange_medium_type, From 7c8f9334316df9cbde977369bf546c4035687fa9 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Mon, 21 Sep 2026 15:12:56 +0200 Subject: [PATCH 06/10] Refactor handleScan: simplify constructor --- .../pretixscan/droid/ui/BaseScanActivity.kt | 22 +++++++------------ .../pretixscan/droid/ui/KioskActivity.kt | 12 ++-------- .../pretixscan/droid/ui/MainActivity.kt | 13 ++--------- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index 4ca3be90..dc808666 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -117,8 +117,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann } handleScan( result, - ReusableMediaType.BARCODE, - null, + ReusableMediaType.BARCODE ) } }) @@ -487,11 +486,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann open fun handleScan( raw_result: String, - source_type: ReusableMediaType, - answers: MutableList?, - ignore_unpaid: Boolean = !conf.unpaidAsk, - exchange_medium_type: ReusableMediaType? = null, - exchange_medium_identifier: String? = null, + source_type: ReusableMediaType ) { if (dialog?.isShowing() == true) { /* @@ -524,8 +519,10 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann raw_result } + val ignore_unpaid = !conf.unpaidAsk + // FIXME: don't play sound for media exchange - if (answers == null && !ignore_unpaid && !conf.offlineMode && conf.sounds) { + if (!ignore_unpaid && !conf.offlineMode && conf.sounds) { mediaPlayers[R.raw.beep]?.start() } @@ -535,7 +532,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann lastIgnoreUnpaid = ignore_unpaid lastScanResult = null - performCheckin(result, source_type, answers, ignore_unpaid, exchange_medium_type, exchange_medium_identifier) + performCheckin(result, source_type, null, ignore_unpaid) } fun performCheckin( @@ -655,7 +652,6 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann handleScan( s, ReusableMediaType.BARCODE, - null, ) } @@ -670,8 +666,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann } handleScan( keyboardBuffer, - ReusableMediaType.BARCODE, - null, + ReusableMediaType.BARCODE ) keyboardBuffer = "" true @@ -767,8 +762,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann handleScan( identifier, - mediaType, - null, + mediaType ) } diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt index 83d6f66f..dda3c903 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/KioskActivity.kt @@ -830,11 +830,7 @@ class KioskActivity : BaseScanActivity() { override fun handleScan( raw_result: String, - source_type: ReusableMediaType, - answers: MutableList?, - ignore_unpaid: Boolean, - exchange_medium_type: ReusableMediaType?, - exchange_medium_identifier: String? + source_type: ReusableMediaType ) { if (conf.requiresPin("settings") && conf.verifyPin(raw_result)) { openMenu(raw_result) @@ -871,11 +867,7 @@ class KioskActivity : BaseScanActivity() { updateUi() super.handleScan( raw_result, - source_type, - answers, - ignore_unpaid, - exchange_medium_type, - exchange_medium_identifier + source_type ) } diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index aff5ed51..ac703b62 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -206,8 +206,7 @@ class MainActivity : BaseScanActivity() { hideSearchCard() handleScan( res.secret!!, - ReusableMediaType.BARCODE, - null, + ReusableMediaType.BARCODE ) } }) @@ -579,10 +578,6 @@ class MainActivity : BaseScanActivity() { override fun handleScan( raw_result: String, source_type: ReusableMediaType, - answers: MutableList?, - ignore_unpaid: Boolean, - exchange_medium_type: ReusableMediaType?, - exchange_medium_identifier: String?, ) { if (dialog?.isShowing() == true) { /* @@ -601,11 +596,7 @@ class MainActivity : BaseScanActivity() { hideSearchCard() super.handleScan( raw_result, - source_type, - answers, - ignore_unpaid, - exchange_medium_type, - exchange_medium_identifier + source_type ) } From b05534f725354e738e1eba74fe7963aedcc39fa8 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 22 Sep 2026 11:47:50 +0200 Subject: [PATCH 07/10] Fix crash on rotation caused by unserializing questions --- .../eu/pretix/pretixscan/droid/AppConfig.kt | 13 +++++++++++ .../pretixscan/droid/ui/MainActivity.kt | 23 ------------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt index 5cf97781..c358d01f 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/AppConfig.kt @@ -8,6 +8,7 @@ import androidx.preference.PreferenceManager import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.datatype.joda.JodaModule import com.fasterxml.jackson.module.kotlin.KotlinFeature import com.fasterxml.jackson.module.kotlin.KotlinModule @@ -15,8 +16,13 @@ import eu.pretix.libpretixsync.api.PretixApi import eu.pretix.libpretixsync.config.ConfigStore import eu.pretix.libpretixnfc.android.platform.AndroidKeyStore import eu.pretix.libpretixnfc.platform.HardwareBackedKeyStore +import eu.pretix.libpretixsync.serialization.JSONArrayDeserializer +import eu.pretix.libpretixsync.serialization.JSONArraySerializer +import eu.pretix.libpretixsync.serialization.JSONObjectDeserializer +import eu.pretix.libpretixsync.serialization.JSONObjectSerializer import eu.pretix.pretixscan.utils.KeystoreHelper import org.joda.time.DateTime +import org.json.JSONArray import org.json.JSONObject data class EventSelection( @@ -38,6 +44,13 @@ val om = ObjectMapper().apply { .configure(KotlinFeature.SingletonSupport, true) .configure(KotlinFeature.StrictNullChecks, false) .build()) + val jsonModule = SimpleModule().apply { + addDeserializer(JSONObject::class.java, JSONObjectDeserializer()) + addDeserializer(JSONArray::class.java, JSONArrayDeserializer()) + addSerializer(JSONObject::class.java, JSONObjectSerializer()) + addSerializer(JSONArray::class.java, JSONArraySerializer()) + } + registerModule(jsonModule) configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) configure(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE, false) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index ac703b62..66f559a9 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -38,19 +38,12 @@ import androidx.databinding.ObservableField import androidx.recyclerview.widget.LinearLayoutManager import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.module.SimpleModule import eu.pretix.libpretixsync.api.PretixApi import eu.pretix.libpretixsync.check.CheckException import eu.pretix.libpretixsync.check.TicketCheckProvider import eu.pretix.libpretixsync.db.Answer import eu.pretix.libpretixsync.db.ReusableMediaType import eu.pretix.libpretixsync.models.db.toModel -import eu.pretix.libpretixsync.serialization.JSONArrayDeserializer -import eu.pretix.libpretixsync.serialization.JSONArraySerializer -import eu.pretix.libpretixsync.serialization.JSONObjectDeserializer -import eu.pretix.libpretixsync.serialization.JSONObjectSerializer import eu.pretix.libpretixui.android.scanning.ScannerView import eu.pretix.pretixscan.droid.* import eu.pretix.pretixscan.droid.databinding.ActivityMainBinding @@ -60,8 +53,6 @@ import eu.pretix.pretixscan.droid.ui.ResultState.* import eu.pretix.pretixscan.droid.ui.info.EventinfoActivity import io.sentry.Sentry import kotlinx.coroutines.launch -import org.json.JSONArray -import org.json.JSONObject import splitties.toast.toast import java.text.SimpleDateFormat import java.time.format.DateTimeFormatter @@ -971,13 +962,6 @@ class MainActivity : BaseScanActivity() { val resultState = savedInstanceState.getString("result_state", "") if (resultState.startsWith("DIALOG_")) { - val module = SimpleModule() - module.addDeserializer(JSONObject::class.java, JSONObjectDeserializer()) - module.addDeserializer(JSONArray::class.java, JSONArrayDeserializer()) - val om = ObjectMapper() - om.registerModule(module) - om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - lastScanCode = savedInstanceState.getString("lastScanCode", null) lastScanSourceType = ReusableMediaType.entries.firstOrNull { it.serverName == savedInstanceState.getString("lastScanType", ReusableMediaType.BARCODE.serverName) } ?: ReusableMediaType.BARCODE lastIgnoreUnpaid = savedInstanceState.getBoolean("ignore_unpaid") @@ -1051,13 +1035,6 @@ class MainActivity : BaseScanActivity() { // we try to serialize all state required to re-create the dialog if the user returns. if (view_data.resultState.get() in listOf(DIALOG_QUESTIONS, DIALOG_EXCHANGE, DIALOG_UNPAID) && dialog != null && lastScanResult != null) { - val module = SimpleModule() - module.addSerializer(JSONObject::class.java, JSONObjectSerializer()) - module.addSerializer(JSONArray::class.java, JSONArraySerializer()) - val om = ObjectMapper() - om.registerModule(module) - om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - outState.putString("result_state", view_data.resultState.get().toString().uppercase()) outState.putString("lastScanCode", lastScanCode) outState.putString("lastScanType", lastScanSourceType.serverName) From 5caf9da82fe1acd7aee2b6eb1d56cd272835815a Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 22 Sep 2026 11:49:59 +0200 Subject: [PATCH 08/10] Fix missing lastScanResult on dialog launch for rotation state saving --- .../main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index 66f559a9..c40f79b2 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -627,6 +627,7 @@ class MainActivity : BaseScanActivity() { stopHidingTimer() if (result.type == TicketCheckProvider.CheckResult.Type.ANSWERS_REQUIRED) { + lastScanResult = result view_data.resultState.set(DIALOG_QUESTIONS) dialog = showQuestionsDialog(result, null, false) { answers -> performCheckin( @@ -641,6 +642,7 @@ class MainActivity : BaseScanActivity() { return } if (result.type == TicketCheckProvider.CheckResult.Type.EXCHANGE_REQUIRED) { + lastScanResult = result view_data.resultState.set(DIALOG_EXCHANGE) dialog = showExchangeDialog(this, result, nfcHandler?.getState()) { mediaIdentifier, mediaType -> hideCard() @@ -658,6 +660,7 @@ class MainActivity : BaseScanActivity() { return } if (result.type == TicketCheckProvider.CheckResult.Type.UNPAID && result.isCheckinAllowed) { + lastScanResult = result view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { stopHidingTimer() From 28364f8fce960ca314b8ca5816946d7ae07e7b13 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 22 Sep 2026 11:50:34 +0200 Subject: [PATCH 09/10] Unify card state (show loading card) for dialogs --- .../eu/pretix/pretixscan/droid/ui/MainActivity.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt index c40f79b2..a1615b30 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/MainActivity.kt @@ -5,7 +5,6 @@ import android.animation.LayoutTransition import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.content.Context -import android.content.DialogInterface import android.content.Intent import android.content.pm.PackageManager import android.content.res.ColorStateList @@ -645,7 +644,7 @@ class MainActivity : BaseScanActivity() { lastScanResult = result view_data.resultState.set(DIALOG_EXCHANGE) dialog = showExchangeDialog(this, result, nfcHandler?.getState()) { mediaIdentifier, mediaType -> - hideCard() + showLoadingCard() performCheckin( lastScanCode, lastScanSourceType, @@ -663,7 +662,7 @@ class MainActivity : BaseScanActivity() { lastScanResult = result view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { - stopHidingTimer() + showLoadingCard() performCheckin( lastScanCode, lastScanSourceType, @@ -987,7 +986,7 @@ class MainActivity : BaseScanActivity() { } dialog = showQuestionsDialog(lastScanResult!!, values, true) { answers -> - stopHidingTimer() + showLoadingCard() performCheckin( lastScanCode, lastScanSourceType, @@ -1000,7 +999,7 @@ class MainActivity : BaseScanActivity() { } else if (resultState == "DIALOG_UNPAID") { view_data.resultState.set(DIALOG_UNPAID) dialog = showUnpaidDialog(this) { - hideCard() + showLoadingCard() performCheckin( lastScanCode, lastScanSourceType, @@ -1013,7 +1012,7 @@ class MainActivity : BaseScanActivity() { view_data.resultState.set(DIALOG_EXCHANGE) reloadNfcHandler() // else nfchandler is null dialog = showExchangeDialog(this, lastScanResult!!, nfcHandler?.getState()) { mediaIdentifier, mediaType -> - hideCard() + showLoadingCard() performCheckin( lastScanCode, lastScanSourceType, From 225d914f664e8c492c388028b8b1ff83058923c1 Mon Sep 17 00:00:00 2001 From: Maximilian Richt Date: Tue, 22 Sep 2026 12:06:16 +0200 Subject: [PATCH 10/10] Simplify scan beep conditions The sound is not played for the exchange anymore, since that uses performCheckin now, the unpaid condition is also moot cause that also continues in performCheckin. --- .../java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt index dc808666..8a279d97 100644 --- a/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt +++ b/pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/ui/BaseScanActivity.kt @@ -521,8 +521,7 @@ abstract class BaseScanActivity : AppCompatActivity(), ReloadableActivity, Scann val ignore_unpaid = !conf.unpaidAsk - // FIXME: don't play sound for media exchange - if (!ignore_unpaid && !conf.offlineMode && conf.sounds) { + if (!conf.offlineMode && conf.sounds) { mediaPlayers[R.raw.beep]?.start() }