some cards no read fix
Build APK / build (push) Failing after 3s

This commit is contained in:
2026-10-01 23:50:40 +05:00
parent 2e04a14ff7
commit 167ed3e67d
4 changed files with 40 additions and 18 deletions
+17 -11
View File
@@ -111,19 +111,25 @@ class MainActivity : ComponentActivity() {
private fun enableReader(a: NfcAdapter) {
val flags = NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_NFC_B or
NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
// Raise the presence-check delay so the system doesn't poll the tag mid-read and drop
// the field during a slow card's dump.
val extras = Bundle().apply {
putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 5000)
}
a.enableReaderMode(this, { tag ->
// Binder thread — safe to block on card I/O here.
// The reader-mode callback runs on a background (binder) thread. Read the card HERE,
// synchronously, rather than offloading to another thread and returning — returning
// early lets the system tear the field down before a slow card finishes, which drops
// the PAN on some cards.
runOnUiThread { if (uiState is UiState.Waiting) uiState = UiState.Waiting(TapState.READING) }
thread {
val result = runCatching { EmvCardReader.read(tag) }.getOrElse {
EmvCardReader.Result(
uid = "", techList = emptyList(), historicalBytes = null, hiLayerResponse = null,
atqa = null, sak = null, apps = emptyList(), fields = emptyList(), apduLog = emptyList(),
error = "Read error: ${it.message ?: it.javaClass.simpleName}",
)
}
runOnUiThread { uiState = UiState.Done(result) }
val result = runCatching { EmvCardReader.read(tag) }.getOrElse {
EmvCardReader.Result(
uid = "", techList = emptyList(), historicalBytes = null, hiLayerResponse = null,
atqa = null, sak = null, apps = emptyList(), fields = emptyList(), apduLog = emptyList(),
error = "Read error: ${it.message ?: it.javaClass.simpleName}",
)
}
}, flags, null)
runOnUiThread { uiState = UiState.Done(result) }
}, flags, extras)
}
}
@@ -85,7 +85,7 @@ object EmvCardReader {
var error: String? = null
try {
iso.connect()
iso.timeout = 5000
iso.timeout = 10000
// 1) PPSE — the payment application directory.
val ppse = exchange(iso, selectName("2PAY.SYS.DDF01"), "SELECT PPSE (2PAY.SYS.DDF01)", log)
@@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.material3.MaterialTheme
@@ -49,6 +50,7 @@ fun NfcCardAnimation(
state: TapState,
label: String,
modifier: Modifier = Modifier,
canvasHeight: Dp = 220.dp,
) {
val cs = MaterialTheme.colorScheme
var now by remember { mutableLongStateOf(0L) }
@@ -72,7 +74,7 @@ fun NfcCardAnimation(
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(220.dp)
.height(canvasHeight)
) {
drawScene(state, elapsed, cs.primary, cs.onSurface, cs.surfaceVariant, cs.error, cs.onPrimary)
}
+19 -5
View File
@@ -1,6 +1,5 @@
package sh.sar.sw9000.ui
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -13,6 +12,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Card
@@ -92,7 +92,8 @@ fun ResultScreen(
NfcCardAnimation(
state = if (result.hasData) TapState.SUCCESS else TapState.ERROR,
label = if (result.hasData) "Card read" else "Couldn't read card",
modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
canvasHeight = 96.dp,
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
)
if (result.error != null && !result.hasData) {
@@ -115,7 +116,13 @@ fun ResultScreen(
.verticalScroll(rememberScrollState())
.padding(16.dp),
) {
if (tab == 0) FormattedView(result) else RawDumpView(result)
// Long-press any value to select and copy it. The inner Column arranges the cards
// vertically — SelectionContainer is a single slot and would otherwise stack them.
SelectionContainer {
Column {
if (tab == 0) FormattedView(result) else RawDumpView(result)
}
}
Spacer(Modifier.height(16.dp))
Button(onClick = onReadAnother, modifier = Modifier.fillMaxWidth()) { Text("Read another card") }
Spacer(Modifier.height(32.dp))
@@ -211,12 +218,19 @@ private fun TlvRow(node: TlvNode, depth: Int) {
@Composable
private fun MonoLabeled(prefix: String, text: String, color: androidx.compose.ui.graphics.Color) {
Row(Modifier.fillMaxWidth().horizontalScroll(rememberScrollState())) {
Row(Modifier.fillMaxWidth()) {
if (prefix.isNotEmpty()) {
Text(prefix, fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = color)
Spacer(Modifier.width(6.dp))
}
Text(text.ifBlank { "(empty)" }, fontFamily = FontFamily.Monospace, fontSize = 12.sp, color = color)
// Wrap long hex rather than scroll it — keeps row heights correct and is easy to select.
Text(
text.ifBlank { "(empty)" },
modifier = Modifier.weight(1f),
fontFamily = FontFamily.Monospace,
fontSize = 12.sp,
color = color,
)
}
}