feat: add nfc step 1
This commit is contained in:
@@ -25,6 +25,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.ContentPaste
|
||||
import androidx.compose.material.icons.filled.Nfc
|
||||
import androidx.compose.material.icons.filled.QrCode
|
||||
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -160,6 +161,36 @@ fun WalletScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// ── NFC reader mode — register on composition, unregister on disposal ────────
|
||||
val nfcAdapter = remember {
|
||||
android.nfc.NfcAdapter.getDefaultAdapter(context)
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
val activity = context as MainActivity
|
||||
nfcAdapter?.enableReaderMode(
|
||||
activity,
|
||||
{ tag -> vm.onNfcTagDiscovered(tag) }, // runs on NFC binder thread
|
||||
android.nfc.NfcAdapter.FLAG_READER_NFC_A or
|
||||
android.nfc.NfcAdapter.FLAG_READER_NFC_B or
|
||||
android.nfc.NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK.inv(), // keep NDEF
|
||||
null
|
||||
)
|
||||
onDispose {
|
||||
nfcAdapter?.disableReaderMode(activity)
|
||||
}
|
||||
}
|
||||
|
||||
val nfcOffer by vm.nfcOfferState.collectAsStateWithLifecycle()
|
||||
|
||||
// Auto-dismiss NFC banner after 8 seconds (same as clipboard)
|
||||
LaunchedEffect(nfcOffer) {
|
||||
if (nfcOffer is NfcOfferState.Detected) {
|
||||
delay(8_000)
|
||||
vm.dismissNfcOffer()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
if (showBottomBar) {
|
||||
@@ -414,6 +445,70 @@ fun WalletScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
// ── NFC banner — floats below clipboard banner, slides in from top ────────────
|
||||
AnimatedVisibility(
|
||||
visible = nfcOffer is NfcOfferState.Detected,
|
||||
enter = slideInVertically(initialOffsetY = { -it }),
|
||||
exit = slideOutVertically(targetOffsetY = { -it }),
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter)
|
||||
.zIndex(1f)
|
||||
.fillMaxWidth()
|
||||
.statusBarsPadding()
|
||||
// push down if clipboard banner is also showing
|
||||
.padding(top = if (clipboardOffer is ClipboardOfferState.Detected) 64.dp else 0.dp)
|
||||
) {
|
||||
if (nfcOffer is NfcOfferState.Detected) {
|
||||
val offer = nfcOffer as NfcOfferState.Detected
|
||||
val label = when (offer.inputType) {
|
||||
is SendInputType.Bolt11 -> "Invoice"
|
||||
is SendInputType.Lnurl -> "LNURL"
|
||||
is SendInputType.LightningAddress -> "Lightning Address"
|
||||
else -> "Payment"
|
||||
}
|
||||
val subLabel = when (offer.inputType) {
|
||||
is SendInputType.Lnurl -> "Tap to open"
|
||||
else -> "Tap to pay"
|
||||
}
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.secondaryContainer, // distinct from clipboard's primaryContainer
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
vm.dismissNfcOffer()
|
||||
vm.scan(offer.raw)
|
||||
// scan() already emits navigateToReceive for LNURL-withdraw,
|
||||
// so we only need to navigate to Send for everything else.
|
||||
// The existing navigateToReceive LaunchedEffect handles BoltCard.
|
||||
navController.navigate(TabItem.Send.route) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Filled.Nfc, contentDescription = null)
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(
|
||||
"$label detected via NFC",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Text(
|
||||
subLabel,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { vm.dismissNfcOffer() }) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Dismiss")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user