Skip to main content

NFC Capture

For scanning the NFC, you must override onNewIntent and onResume methods on the Activity class are required.

Flow overviewFlowcharts of the NFC scan: after an ID capture, or directly with known MRZ data (no ID photo). Expand to view them, click a chart to enlarge.

ID capture with NFC flow

  • Configure with ID_CAPTURE and NFC_SCAN, set the event listener with AmaniEvent().setListener(...) (the verdicts arrive there, asynchronously, during the whole session), then start the session.
  • N1: set the manual button timeout with setManualCropTimeOut(...), then capture the ID front and back. No bitmap yet means the user has not captured yet; the module stays open. After the timeout set with setManualCropTimeOut(...), the user can capture with the manual button. If there is still no capture, count 20–30 seconds on your side after the button appears and handle the process as you need.
  • N2: read the MRZ with getMRZ(...); if it fails, let the user type it or capture the ID again.
  • N3: if the device has NFC turned on, enable the foreground dispatch in onResume and disable it in onPause; otherwise skip the chip.
  • N4: onNewIntent brings the tag when the card touches the phone; start ScanNFC().start(...) there with the MRZ values from getMRZ (they are String?: check them in N2); retry if it fails.
  • N5, or N6 + N7: upload the chip on its own, or merge it into the ID upload with withNFC(true). On V2 a successful upload means the document reached the server; its verdict arrives later in stepsResult. If the upload fails or the step is REJECTED, do not call upload() again: the data is cleared after it. Open the ID capture and the chip scan again (a new fragment, or the old one with your own fragment handling, e.g. popBackStack), capture, then upload, up to your own max attempt.

NFC scan with known MRZ data (no ID photo): when the birth date, expiry date and document number are already known, the chip can be scanned directly, without capturing the ID or calling getMRZ.

NFC scan with known MRZ data

  • Configure with NFC_SCAN only, set the event listener, start the session.
  • C1: have the three MRZ values ready (birth date and expiry date as yyMMdd, document number) from your own data.
  • C2: if the device has NFC turned on, enable the foreground dispatch in onResume and disable it in onPause.
  • C3: onNewIntent brings the tag when the card touches the phone; start ScanNFC().start(...) there with the three values. Keeps failing? The values may be wrong.
  • C4: upload the chip with Amani.sharedInstance().ScanNFC().upload(...); its verdict arrives later in stepsResult. If the upload fails or the step is REJECTED, do not call upload() again: the data is cleared after it. Open the chip scan again (a new fragment, or the old one with your own fragment handling, e.g. popBackStack), capture, then upload, up to your own max attempt.

Starting the capture​

↑ Flow steps N3 N4 · C2 C3

Add the imports below:

import ai.amani.sdk.Amani
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_MUTABLE
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import android.os.Parcelable

Then override the Activity lifecycle methods as shown below.

Overriding the onResume() to enable NFC Adapter​

  • To initiate NFC scanning, NFCAdapter() is enabled in onResume(), taking into account the Fragment lifecycle.
override fun onResume() {
super.onResume()
val adapter = NfcAdapter.getDefaultAdapter(this)
if (adapter != null) {
val intent = Intent(applicationContext, this.javaClass)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(this, 0, Intent(this, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), FLAG_MUTABLE)
} else{
PendingIntent.getActivity(this, 0, Intent(this, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
}
val filter = arrayOf(arrayOf("android.nfc.tech.IsoDep"))
adapter.enableForegroundDispatch(this, pendingIntent, null, filter)
}
}

Overriding the onPause() to disable NFC Adapter​

  • In cases where the user moves away from the NFC screen, NFCAdapter() is disabled in onPause(). In this way, we remove the user's ability to accidentally/intentionally scan NFC after the NFC process.
override fun onPause() {
super.onPause()
val adapter = NfcAdapter.getDefaultAdapter(this)
adapter?.let {
it.disableForegroundDispatch(this)
}
}

Overriding the onNewIntent for checking the Tag​

  • The onNewIntent method is automatically triggered by bringing the ID card closer to the NFC antenna area of the device. In this scenario, the NFC scanning process is started by passing the tag taken from the intent and the data group taken from the MRZ to the NFC scan method.

[!NOTE] How to get MRZ data? You can access MRZ with the getMRZ() function in the IDCapture() module. Click on the link for detailed information.

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val tag = intent?.extras?.parcelable<Tag>(NfcAdapter.EXTRA_TAG) ?: return
Amani.sharedInstance().ScanNFC().start(
tag,
context,
birthDate, //MRZ data as YYMMDD format
expireDate, //MRZ data as YYMMDD format
documentNumber//MRZ data
) { bitmap, isSuccess, error ->
if (isSuccess) onComplete.invoke()
else onFailure.invoke()
}
}
  • Intent/Bundle Extentions that may help to get tag
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? = when {
Build.VERSION.SDK_INT >= 33 -> getParcelableExtra(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T
}

inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? = when {
Build.VERSION.SDK_INT >= 33 -> getParcelable(key, T::class.java)
else -> @Suppress("DEPRECATION") getParcelable(key) as? T
}

Uploading the NFC Data​

↑ Flow steps N5 · C4

  • The upload method has to be called after the result of the start method is successful. Otherwise you'll encounter an error in the Errors() object when called.

Add the imports below:

import ai.amani.sdk.Amani

Then upload the scanned NFC data as shown below:

Amani.sharedInstance().ScanNFC().upload(
requireActivity(),
"document type" // Supplied by Amani. XXX_NF_0 is an example.
) { uploadNFCSuccess ->
if (uploadNFCSuccess) {
Log.i(TAG, "NFC is uploaded")
} else {
Log.e(TAG, "NFC could not be uploaded")
}
}
note

A true result means different things per backend version. V2: the document reached the server, not that it was approved; verification runs asynchronously on the backend, so the outcome arrives later through the event listener rather than here. V1: the upload response status was OK or PENDING_REVIEW, so true already carries the verdict.

Alternatively, if you need the ID of the uploaded document, pass the uploadCallBack { ... } helper instead of the callback above. Everything else stays the same:

import ai.amani.sdk.interfaces.uploadCallBack

Amani.sharedInstance().ScanNFC().upload(
requireActivity(),
"document type", // Supplied by Amani. XXX_NF_0 is an example.
uploadCallBack { isSuccess, documentID ->
// isSuccess is true when the document is uploaded
// documentID is the ID of the uploaded document
}
)