Implement emulation (hopefully!)

This commit is contained in:
Kodi Craft 2025-08-25 18:53:18 +02:00
parent 6481878cee
commit de97b9b7e1
Signed by: kodi
GPG Key ID: 69D9EED60B242822
2 changed files with 37 additions and 12 deletions

View File

@ -50,6 +50,7 @@ import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -83,7 +84,7 @@ class MainActivity : ComponentActivity() {
lateinit var adapter: NfcAdapter lateinit var adapter: NfcAdapter
var canEmulateCard: Boolean = false var canEmulateCard: Boolean = false
var emulationActive: Boolean = false var emulationCardID: String? = null
private val TAG = "AimeReader" private val TAG = "AimeReader"
private val FILENAME = "cards.json" private val FILENAME = "cards.json"
@ -158,7 +159,16 @@ class MainActivity : ComponentActivity() {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var snackbarHostState = remember { SnackbarHostState() } var snackbarHostState = remember { SnackbarHostState() }
var selectedCardIndexForEmulation by rememberSaveable { mutableStateOf(-1) } var selectedCardIndexForEmulation by rememberSaveable { mutableIntStateOf(-1) }
LaunchedEffect(selectedCardIndexForEmulation) {
if (selectedCardIndexForEmulation == -1) {
this@MainActivity.emulationCardID = null
} else {
this@MainActivity.emulationCardID = cards[selectedCardIndexForEmulation].getHexID()
}
updateEmulation()
}
AimeReaderTheme { AimeReaderTheme {
Scaffold ( Scaffold (
@ -261,17 +271,28 @@ class MainActivity : ComponentActivity() {
adapter.disableForegroundDispatch(this) adapter.disableForegroundDispatch(this)
val nfcfCardEmulation = NfcFCardEmulation.getInstance(adapter) val nfcfCardEmulation = NfcFCardEmulation.getInstance(adapter)
nfcfCardEmulation.disableService(this) nfcfCardEmulation.disableService(this)
Log.i(TAG, "Pausing activity, emulation disabled")
} }
public override fun onResume() { public override fun onResume() {
super.onResume() super.onResume()
adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray) adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
val nfcfCardEmulation = NfcFCardEmulation.getInstance(adapter) updateEmulation()
nfcfCardEmulation.registerSystemCodeForService(ComponentName(this, AimeHostApduService::class.java), "4000")
Log.d(TAG, nfcfCardEmulation.setNfcid2ForService(ComponentName(this, AimeHostApduService::class.java), "02FE000000000000").toString())
Log.d(TAG, nfcfCardEmulation.enableService(this, ComponentName(this, AimeHostApduService::class.java)).toString())
Log.i(TAG, "activity resumed")
} }
fun updateEmulation() {
val nfcfCardEmulation = NfcFCardEmulation.getInstance(adapter)
if (emulationCardID != null) {
Log.i(TAG, "Enabling emulation of card $emulationCardID")
nfcfCardEmulation.registerSystemCodeForService(ComponentName(this, AimeHostApduService::class.java), "4000")
Log.d(TAG, nfcfCardEmulation.setNfcid2ForService(ComponentName(this, AimeHostApduService::class.java), emulationCardID).toString())
Log.d(TAG, nfcfCardEmulation.enableService(this, ComponentName(this, AimeHostApduService::class.java)).toString())
} else {
Log.i(TAG, "Disabling card emulation")
nfcfCardEmulation.disableService(this)
}
}
} }
@Composable @Composable
@ -288,7 +309,7 @@ fun CardList(
Column ( Column (
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
modifier = Modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .padding(innerPadding)
) { ) {
@ -297,7 +318,7 @@ fun CardList(
} }
} else { } else {
LazyColumn ( LazyColumn (
modifier = Modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(innerPadding) .padding(innerPadding)
) { ) {
@ -327,7 +348,7 @@ fun CardDisplay(
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Row ( Row (
modifier = Modifier modifier = modifier
.padding(horizontal = 8.dp, vertical = 8.dp) .padding(horizontal = 8.dp, vertical = 8.dp)
.fillMaxWidth() .fillMaxWidth()
.clip(shape = RoundedCornerShape(16.dp)) .clip(shape = RoundedCornerShape(16.dp))
@ -383,7 +404,7 @@ fun CardDisplay(
@Preview @Preview
@Composable @Composable
fun CardDisplayPreview(modifier: Modifier = Modifier) { fun CardDisplayPreview(modifier: Modifier = Modifier) {
Column(modifier = Modifier.fillMaxSize()) { Column(modifier = modifier.fillMaxSize()) {
CardDisplay(SavedCard(byteArrayOf(0x02, 0xFE.toByte(), 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)), true, true, {}, {}, {}) CardDisplay(SavedCard(byteArrayOf(0x02, 0xFE.toByte(), 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)), true, true, {}, {}, {})
CardDisplay(SavedCard(byteArrayOf(0x02, 0xFE.toByte(), 0x12, 0x34, 0x56, 0x78, 0x12, 0x34)), true, false, {}, {}, {}) CardDisplay(SavedCard(byteArrayOf(0x02, 0xFE.toByte(), 0x12, 0x34, 0x56, 0x78, 0x12, 0x34)), true, false, {}, {}, {})
CardDisplay(SavedCard(byteArrayOf(0x02, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)), true, false, {}, {}, {}) CardDisplay(SavedCard(byteArrayOf(0x02, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)), true, false, {}, {}, {})

View File

@ -1,6 +1,5 @@
package me.kdcf.aimereader package me.kdcf.aimereader
import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -25,6 +24,11 @@ class SavedCard(private var code: ByteArray, private var friendlyName: String =
return getPadded().chunked(4).joinToString(" ") return getPadded().chunked(4).joinToString(" ")
} }
@OptIn(ExperimentalStdlibApi::class)
fun getHexID(): String {
return getCode().toHexString()
}
fun getDisplay(): String { fun getDisplay(): String {
if (friendlyName.isEmpty()) { if (friendlyName.isEmpty()) {
return getSpaced() return getSpaced()