Este documento describe las tareas básicas de NFC que realizas en Android. Además, se explica cómo enviar y recibir datos NFC en forma de mensajes NDEF y se describen las APIs del framework de Android que admiten estas funciones. Para obtener temas más avanzados, incluido un debate sobre cómo trabajar con datos que no son NDEF, consulta NFC avanzados.
Hay dos casos prácticos importantes para tener en cuenta al trabajar con datos NDEF y Android:
- Lectura de datos NDEF de una etiqueta NFC
- Transmisión de mensajes NDEF de un dispositivo a otro con Android BeamTM
La lectura de datos NDEF de una etiqueta NFC se controla con el sistema de envío de etiquetas, que analiza las etiquetas NFC detectadas, categoriza los datos de forma adecuada e inicia una aplicación que está interesada en los datos categorizados. Una aplicación que desea controlar la etiqueta NFC escaneada puede declarar un filtro de intents y solicitar el control de los datos.
La función Android BeamTM permite que un dispositivo envíe un mensaje NDEF a otro dispositivo presionando físicamente los dispositivos. Esta interacción proporciona una forma más fácil de enviar datos que otras tecnologías inalámbricas, como Bluetooth, ya que con NFC no se requiere la vinculación ni el descubrimiento manual del dispositivo. La conexión se inicia automáticamente cuando dos dispositivos están dentro del alcance. Android Beam está disponible a través de un conjunto de APIs de NFC, por lo que cualquier aplicación puede transmitir información entre dispositivos. Por ejemplo, las aplicaciones de Contactos, Navegador y YouTube usan Android Beam para compartir contactos, páginas web y videos con otros dispositivos.
Sistema de envío de etiquetas
Los dispositivos con Android suelen buscar etiquetas NFC cuando la pantalla está desbloqueada, a menos que NFC esté inhabilitado en el menú Configuración del dispositivo. Cuando un dispositivo Android descubre una etiqueta NFC, el comportamiento deseado es que la actividad más adecuada maneje el intent sin preguntarle al usuario qué aplicación usar. Debido a que los dispositivos escanean las etiquetas NFC en un alcance muy corto, es probable que hacer que los usuarios seleccionen manualmente una actividad los obligue a alejar el dispositivo de la etiqueta y interrumpir la conexión. Debes desarrollar tu actividad para controlar solo las etiquetas NFC que le interesan a tu actividad para evitar que aparezca el selector de actividad.
Para ayudarte con este objetivo, Android proporciona un sistema especial de envío de etiquetas que analiza las etiquetas NFC escaneadas y, luego, intenta ubicar las aplicaciones que estén interesadas en los datos analizados. Para ello, sigue estos pasos:
- Análisis de la etiqueta NFC y averiguación del tipo de MIME o URI que identifica la carga útil de datos en la etiqueta
- Encapsulando el tipo MIME o URI y la carga útil en un intent. Estos dos primeros pasos se describen en Cómo se asignan las etiquetas NFC a los tipos de MIME y los URI.
- Iniciando una actividad basada en el intent. Esto se describe en Cómo se envían las etiquetas NFC a las aplicaciones.
Cómo se asignan las etiquetas NFC a los tipos MIME y URI
Antes de comenzar a escribir tus aplicaciones NFC, es importante comprender los diferentes tipos de etiquetas NFC, la forma en que el sistema de envío de etiquetas analiza las etiquetas NFC y el trabajo especial que realiza el sistema de envío de etiquetas cuando detecta un mensaje NDEF. Las etiquetas NFC vienen en una amplia variedad de tecnologías y también pueden tener datos escritos de muchas maneras diferentes. Android tiene la mayor compatibilidad con el estándar NDEF, que se define en el Foro NFC.
Los datos NDEF se encapsulan dentro de un mensaje (NdefMessage
) que contiene uno o más registros (NdefRecord
). Cada registro NDEF debe tener el formato indicado en la especificación del tipo de registro que deseas crear. Android también admite otros tipos de etiquetas que no contienen datos NDEF, con los que puedes trabajar usando las clases incluidas en el paquete android.nfc.tech
. Para obtener más información sobre estas tecnologías, consulta el tema Conceptos avanzados de NFC. Trabajar con estos otros tipos de etiquetas implica escribir tu propia pila de protocolos para comunicarte con las etiquetas, por lo que te recomendamos que uses NDEF siempre que sea posible para facilitar el desarrollo y brindar la máxima compatibilidad con los dispositivos con Android.
Nota: Para descargar las especificaciones completas de NDEF, ve al sitio Especificaciones del foro de NFC y documentos de la aplicación y consulta Cómo crear tipos comunes de registros NDEF para ver ejemplos de cómo construir registros de NDEF.
Ahora que tienes más información sobre las etiquetas NFC, consulta las siguientes secciones que describen en más detalle cómo Android maneja las etiquetas con formato NDEF. Cuando un dispositivo Android escanea una etiqueta NFC que contiene datos con formato NDEF, analiza el mensaje y trata de descubrir el tipo de MIME de los datos o el URI de identificación. Para ello, el sistema lee el primer NdefRecord
dentro de la NdefMessage
para determinar cómo interpretar todo el mensaje NDEF (un mensaje NDEF puede tener varios registros NDEF). En un mensaje NDEF bien formado, el primer NdefRecord
contiene los siguientes campos:
- TNF (Formato de nombre de tipo) de 3 bits
- Indica cómo interpretar el campo de tipo de longitud variable. Los valores válidos se describen en la Tabla 1.
- Tipo de longitud variable
- Describe el tipo de registro. Si usas
TNF_WELL_KNOWN
, usa este campo para especificar la definición de tipo de registro (RTD). Los valores válidos de RTD se describen en la tabla 2. - ID de longitud variable
- Un identificador único para el registro. Este campo no se usa con frecuencia, pero si necesitas identificar una etiqueta de forma única, puedes crear un ID para ella.
- Carga útil de longitud variable
- La carga útil de datos reales que deseas leer o escribir. Un mensaje NDEF puede contener varios registros NDEF, así que no des por sentado que la carga útil completa está en el primer registro NDEF del mensaje NDEF.
El sistema de envío de etiquetas usa los campos TNF y de tipo para intentar asignar un tipo MIME o URI al mensaje NDEF. Si tiene éxito, encapsula esa información dentro de un intent ACTION_NDEF_DISCOVERED
junto con la carga útil real. Sin embargo, hay casos en los que el sistema de envío de etiquetas no puede determinar el tipo de datos según el primer registro NDEF. Esto sucede cuando los datos NDEF no se pueden asignar a un tipo MIME o URI, o cuando la etiqueta NFC no contiene datos NDEF. En esos casos, un objeto Tag
que tiene información sobre las tecnologías de la etiqueta y la carga útil se encapsula dentro de un intent ACTION_TECH_DISCOVERED
.
En la Tabla 1, se describe cómo el sistema de envío de etiquetas asigna los campos TNF y de tipo a los tipos de MIME o URI. También describe qué TNF no se puede asignar a un tipo MIME o URI.
En estos casos, el sistema de envío de etiquetas recurre a ACTION_TECH_DISCOVERED
.
Por ejemplo, si el sistema de envío de etiquetas encuentra un registro de tipo TNF_ABSOLUTE_URI
, asigna el campo de tipo de longitud variable de ese registro a un URI. El sistema de envío de etiquetas encapsula ese URI en el campo de datos de un intent ACTION_NDEF_DISCOVERED
junto con otra información sobre la etiqueta, como la carga útil. Por otro lado, si encuentra un registro de tipo TNF_UNKNOWN
, crea un intent que encapsula las tecnologías de la etiqueta.
Tabla 1: Archivos TNF compatibles y sus mapeos
Formato de nombre de tipo (TNF) | Mapeo |
---|---|
TNF_ABSOLUTE_URI |
Este URI se basa en el campo de tipo. |
TNF_EMPTY |
Recurre a ACTION_TECH_DISCOVERED . |
TNF_EXTERNAL_TYPE |
Este URI se basa en el URN en el campo de tipo. El URN está codificado en el campo de tipo NDEF en una forma abreviada: <domain_name>:<service_name> .
Android lo asigna a un URI en la forma: vnd.android.nfc://ext/<domain_name>:<service_name> . |
TNF_MIME_MEDIA |
Este tipo de MIME se basa en el campo de tipo. |
TNF_UNCHANGED |
No es válido en el primer registro, por lo que recurre a ACTION_TECH_DISCOVERED . |
TNF_UNKNOWN |
Recurre a ACTION_TECH_DISCOVERED . |
TNF_WELL_KNOWN |
Tipo de MIME o URI según la definición de tipo de registro (RTD), que estableces en el campo de tipo. Consulta la Tabla 2 para obtener más información sobre las RTD disponibles y sus asignaciones. |
Tabla 2: RTD admitidas para TNF_WELL_KNOWN y sus asignaciones
Definición de tipo de registro (RTD) | Mapeo |
---|---|
RTD_ALTERNATIVE_CARRIER |
Recurre a ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_CARRIER |
Recurre a ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_REQUEST |
Recurre a ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_SELECT |
Recurre a ACTION_TECH_DISCOVERED . |
RTD_SMART_POSTER |
Este URI se basa en el análisis de la carga útil. |
RTD_TEXT |
Es el tipo MIME de text/plain . |
RTD_URI |
Este tipo de URI se basa en la carga útil. |
Cómo se envían las etiquetas NFC a las aplicaciones
Cuando el sistema de envío de etiquetas termina de crear un intent que encapsula la etiqueta NFC y su información de identificación, envía el intent a una aplicación interesada que lo filtre. Si más de una aplicación puede controlar el intent, se presenta el Selector de actividad para que el usuario pueda elegir la actividad. El sistema de envío de etiquetas define tres intents, que se enumeran de mayor a menor prioridad:
-
ACTION_NDEF_DISCOVERED
: Este intent se usa para iniciar una actividad cuando se analiza una etiqueta que contiene una carga útil NDEF y es de un tipo reconocido. Este es el intent de mayor prioridad, y el sistema de envío de etiquetas intenta iniciar una actividad con él antes que con cualquier otro, siempre que sea posible. ACTION_TECH_DISCOVERED
: Si no se registra ninguna actividad para controlar el intentACTION_NDEF_DISCOVERED
, el sistema de envío de etiquetas intenta iniciar una aplicación con este intent. Este intent también se inicia directamente (sin iniciarACTION_NDEF_DISCOVERED
primero) si la etiqueta que se escanea contiene datos NDEF que no se pueden asignar a un tipo MIME o URI, o si la etiqueta no contiene datos NDEF, pero es de una tecnología de etiquetas conocida.ACTION_TAG_DISCOVERED
: Se inicia este intent si ninguna actividad controla los intentsACTION_NDEF_DISCOVERED
oACTION_TECH_DISCOVERED
.
La forma básica en que funciona el sistema de envíos de etiquetas es la siguiente:
- Intenta iniciar una actividad con el intent que creó el sistema de envío de etiquetas cuando se analizó la etiqueta NFC (ya sea
ACTION_NDEF_DISCOVERED
oACTION_TECH_DISCOVERED
). - Si no hay actividades que filtren ese intent, intenta iniciar una actividad con el siguiente intent de menor prioridad (
ACTION_TECH_DISCOVERED
oACTION_TAG_DISCOVERED
) hasta que una aplicación filtre por el intent o hasta que el sistema de envío de etiquetas pruebe todos los intents posibles. - Si no se filtran aplicaciones para ninguno de los intents, no hagas nada.
![](https://developer.android.com/static/images/nfc_tag_dispatch.png?authuser=5&hl=es)
Siempre que sea posible, trabaja con mensajes NDEF y el intent ACTION_NDEF_DISCOVERED
, ya que es el más específico de los tres. Este intent te permite iniciar tu aplicación en un momento más apropiado que los otros dos, lo que brinda al usuario una mejor experiencia.
Cómo solicitar acceso NFC en el manifiesto de Android
Para poder acceder al hardware NFC de un dispositivo y manejar adecuadamente los intents NFC, declara estos elementos en tu archivo AndroidManifest.xml
:
- El elemento NFC
<uses-permission>
para acceder al hardware NFC:<uses-permission android:name="android.permission.NFC" />
- La versión mínima de SDK con la que es compatible tu aplicación. El nivel de API 9 solo admite el envío limitado de etiquetas a través de
ACTION_TAG_DISCOVERED
y otorga acceso a los mensajes NDEF a través delEXTRA_NDEF_MESSAGES
adicional. No se puede acceder a ninguna otra propiedad de etiqueta ni a operaciones de E/S. El nivel de API 10 incluye compatibilidad completa con el lector y el escritor, así como la inserción de NDEF en primer plano, y el nivel de API 14 proporciona una manera más fácil de enviar mensajes NDEF a otros dispositivos con Android Beam y métodos de conveniencia adicionales para crear registros NDEF.<uses-sdk android:minSdkVersion="10"/>
- El elemento
uses-feature
para que tu aplicación aparezca en Google Play solo para dispositivos que tienen hardware NFC:<uses-feature android:name="android.hardware.nfc" android:required="true" />
Si tu aplicación usa la funcionalidad NFC, pero esta no es fundamental para la aplicación, puedes omitir el elemento
uses-feature
y verificar la disponibilidad de NFC en el tiempo de ejecución verificando sigetDefaultAdapter()
esnull
.
Cómo filtrar por intents de NFC
Para iniciar tu aplicación cuando se escanee una etiqueta NFC que deseas manejar, tu aplicación puede filtrar uno, dos o los tres intents NFC en el manifiesto de Android. Sin embargo, por lo general, deseas filtrar el intent ACTION_NDEF_DISCOVERED
para tener el mayor control de cuándo se inicia tu aplicación. El intent ACTION_TECH_DISCOVERED
es un resguardo para ACTION_NDEF_DISCOVERED
cuando no hay filtros de aplicaciones para ACTION_NDEF_DISCOVERED
o cuando la carga útil no es NDEF. Filtrar por ACTION_TAG_DISCOVERED
suele ser una categoría demasiado general para filtrar. Muchas aplicaciones se filtrarán por ACTION_NDEF_DISCOVERED
o ACTION_TECH_DISCOVERED
antes de ACTION_TAG_DISCOVERED
, por lo que tu aplicación tiene una baja probabilidad de iniciarse. ACTION_TAG_DISCOVERED
solo está disponible como último recurso para que las aplicaciones filtren cuando no se instalan otras aplicaciones para manejar el intent ACTION_NDEF_DISCOVERED
o ACTION_TECH_DISCOVERED
.
Debido a que las implementaciones de etiquetas NFC varían y, muchas veces, no están bajo tu control, esto no siempre es posible, por lo que puedes recurrir a los otros dos intents cuando sea necesario. Cuando tienes control sobre los tipos de etiquetas y datos escritos, te recomendamos que uses NDEF para dar formato a tus etiquetas. Las siguientes secciones describen cómo filtrar para cada tipo de intent.
ACTION_NDEF_DISCOVERED
Para filtrar por intents ACTION_NDEF_DISCOVERED
, declara el filtro de intents junto con el tipo de datos que deseas filtrar. El siguiente ejemplo filtra los intents ACTION_NDEF_DISCOVERED
con un tipo de MIME text/plain
:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
En el siguiente ejemplo, se filtra un URI en el formato de https://developer.android.com/index.html
.
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
ACTION_TECH_DISCOVERED
Si tu actividad filtra el intent ACTION_TECH_DISCOVERED
, debes crear un archivo de recursos XML que especifique las tecnologías que admite tu actividad dentro de un conjunto tech-list
. Tu actividad se considera una coincidencia si un conjunto tech-list
es un subconjunto de tecnologías compatibles con la etiqueta, que puedes obtener llamando a getTechList()
.
Por ejemplo, si la etiqueta que se analiza es compatible con MifareClassic, NdefFormatable y NfcA, tu conjunto tech-list
debe especificar las tres, dos o una de las tecnologías (y nada más) para que se establezca la coincidencia de tu actividad.
Los siguientes ejemplos definen todas las tecnologías. Debes quitar las que tu etiqueta NFC no admita. Guarda este archivo (puedes asignarle el nombre que desees) en la carpeta <project-root>/res/xml
.
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.IsoDep</tech> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.NfcF</tech> <tech>android.nfc.tech.NfcV</tech> <tech>android.nfc.tech.Ndef</tech> <tech>android.nfc.tech.NdefFormatable</tech> <tech>android.nfc.tech.MifareClassic</tech> <tech>android.nfc.tech.MifareUltralight</tech> </tech-list> </resources>
También puedes especificar múltiples conjuntos tech-list
. Cada uno de los conjuntos tech-list
se considera de forma independiente, y tu actividad se considera una coincidencia si algún conjunto tech-list
es un subconjunto de las tecnologías que muestra getTechList()
. Esto proporciona la semántica de AND
y OR
para tecnologías coincidentes. El siguiente ejemplo coincide con las etiquetas que pueden admitir las tecnologías NfcA y Ndef, o que pueden admitir las tecnologías NfcB y Ndef:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> </resources>
En tu archivo AndroidManifest.xml
, especifica el archivo de recursos que acabas de crear en el elemento <meta-data>
dentro del elemento <activity>
, como en el siguiente ejemplo:
<activity> ... <intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED"/> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> ... </activity>
Para obtener más información sobre cómo trabajar con tecnologías de etiquetas y el intent ACTION_TECH_DISCOVERED
, consulta Cómo trabajar con tecnologías de etiquetas compatibles en el documento Avanzado de NFC.
ACTION_TAG_DISCOVERED
Para filtrar por ACTION_TAG_DISCOVERED
, usa el siguiente filtro de intents:
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
Cómo obtener información de intents
Si se inicia una actividad debido a un intent NFC, puedes obtener información sobre la etiqueta NFC escaneada del intent. Los intents pueden contener los siguientes servicios adicionales según la etiqueta escaneada:
EXTRA_TAG
(obligatorio): Es un objetoTag
que representa la etiqueta analizada.EXTRA_NDEF_MESSAGES
(opcional): Es un array de mensajes NDEF analizados a partir de la etiqueta. Este adicional es obligatorio para los intentsACTION_NDEF_DISCOVERED
.EXTRA_ID
(opcional): Es el ID de bajo nivel de la etiqueta.
Para obtener estos extras, verifica si tu actividad se inició con uno de los intents de NFC para asegurarte de que se escaneó una etiqueta y, luego, obtén los extras del intent. En el siguiente ejemplo, se verifica el intent ACTION_NDEF_DISCOVERED
y se obtienen los mensajes NDEF de un intent adicional.
Kotlin
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) ... if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages -> val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage } // Process the messages array. ... } } }
Java
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); ... if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMessages != null) { NdefMessage[] messages = new NdefMessage[rawMessages.length]; for (int i = 0; i < rawMessages.length; i++) { messages[i] = (NdefMessage) rawMessages[i]; } // Process the messages array. ... } } }
Como alternativa, puedes obtener un objeto Tag
del intent, que contendrá la carga útil y te permitirá enumerar las tecnologías de la etiqueta:
Kotlin
val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Cómo crear tipos comunes de registros NDEF
En esta sección, se describe cómo crear tipos comunes de registros NDEF para ayudarte cuando escribas en etiquetas NFC o envíes datos con Android Beam. A partir de Android 4.0 (nivel de API 14), el método createUri()
está disponible para ayudarte a crear registros URI automáticamente. A partir de Android 4.1 (nivel de API 16), createExternal()
y createMime()
están disponibles para ayudarte a crear registros MIME y de tipo externo NDEF. Usa estos métodos auxiliares siempre que sea posible para evitar errores cuando crees registros NDEF de forma manual.
En esta sección también se describe cómo crear el filtro de intents correspondiente para el registro. Todos estos ejemplos de registros NDEF deben estar en el primer registro NDEF del mensaje NDEF que estás escribiendo en una etiqueta o transmisión.
TNF_ABSOLUTE_URI
Nota: Te recomendamos usar el tipo RTD_URI
en lugar de TNF_ABSOLUTE_URI
, ya que es más eficiente.
Puedes crear un registro NDEF TNF_ABSOLUTE_URI
de la siguiente manera:
Kotlin
val uriRecord = ByteArray(0).let { emptyByteArray -> NdefRecord( TNF_ABSOLUTE_URI, "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")), emptyByteArray, emptyByteArray ) }
Java
NdefRecord uriRecord = new NdefRecord( NdefRecord.TNF_ABSOLUTE_URI , "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")), new byte[0], new byte[0]);
El filtro de intents para el registro NDEF anterior se vería así:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
TNF_MIME_MEDIA
Puedes crear un registro NDEF TNF_MIME_MEDIA
de las siguientes maneras:
Con el método createMime()
:
Kotlin
val mimeRecord = NdefRecord.createMime( "application/vnd.com.example.android.beam", "Beam me up, Android".toByteArray(Charset.forName("US-ASCII")) )
Java
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
Creando el NdefRecord
de forma manual:
Kotlin
val mimeRecord = Charset.forName("US-ASCII").let { usAscii -> NdefRecord( NdefRecord.TNF_MIME_MEDIA, "application/vnd.com.example.android.beam".toByteArray(usAscii), ByteArray(0), "Beam me up, Android!".toByteArray(usAscii) ) }
Java
NdefRecord mimeRecord = new NdefRecord( NdefRecord.TNF_MIME_MEDIA , "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")), new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
El filtro de intents para el registro NDEF anterior se vería así:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/vnd.com.example.android.beam" /> </intent-filter>
TNF_WELL_KNOWN con RTD_TEXT
Puedes crear un registro NDEF TNF_WELL_KNOWN
de la siguiente manera:
Kotlin
fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord { val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII")) val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16") val textBytes = payload.toByteArray(utfEncoding) val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7 val status = (utfBit + langBytes.size).toChar() val data = ByteArray(1 + langBytes.size + textBytes.size) data[0] = status.toByte() System.arraycopy(langBytes, 0, data, 1, langBytes.size) System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size) return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data) }
Java
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); byte[] textBytes = payload.getBytes(utfEncoding); int utfBit = encodeInUtf8 ? 0 : (1 << 7); char status = (char) (utfBit + langBytes.length); byte[] data = new byte[1 + langBytes.length + textBytes.length]; data[0] = (byte) status; System.arraycopy(langBytes, 0, data, 1, langBytes.length); System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; }
El filtro de intents para el registro NDEF anterior se vería así:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
TNF_WELL_KNOWN con RTD_URI
Puedes crear un registro NDEF TNF_WELL_KNOWN
de las siguientes maneras:
Con el método createUri(String)
:
Kotlin
val rtdUriRecord1 = NdefRecord.createUri("https://example.com")
Java
NdefRecord rtdUriRecord1 = NdefRecord.createUri("https://example.com");
Con el método createUri(Uri)
:
Kotlin
val rtdUriRecord2 = Uri.parse("https://example.com").let { uri -> NdefRecord.createUri(uri) }
Java
Uri uri = Uri.parse("https://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
Creando el NdefRecord
de forma manual:
Kotlin
val uriField = "example.com".toByteArray(Charset.forName("US-ASCII")) val payload = ByteArray(uriField.size + 1) //add 1 for the URI Prefix payload [0] = 0x01 //prefixes https://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.size) //appends URI to payload val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)
Java
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix payload[0] = 0x01; //prefixes https://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload NdefRecord rtdUriRecord = new NdefRecord( NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
El filtro de intents para el registro NDEF anterior se vería así:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="https" android:host="example.com" android:pathPrefix="" /> </intent-filter>
TNF_EXTERNAL_TYPE
Puedes crear un registro NDEF TNF_EXTERNAL_TYPE
de las siguientes maneras:
Con el método createExternal()
:
Kotlin
var payload: ByteArray //assign to your data val domain = "com.example" //usually your app's package name val type = "externalType" val extRecord = NdefRecord.createExternal(domain, type, payload)
Java
byte[] payload; //assign to your data String domain = "com.example"; //usually your app's package name String type = "externalType"; NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
Creando el NdefRecord
de forma manual:
Kotlin
var payload: ByteArray ... val extRecord = NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".toByteArray(Charset.forName("US-ASCII")), ByteArray(0), payload )
Java
byte[] payload; ... NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")), new byte[0], payload);
El filtro de intents para el registro NDEF anterior se vería así:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="vnd.android.nfc" android:host="ext" android:pathPrefix="/com.example:externalType"/> </intent-filter>
Usa TNF_EXTERNAL_TYPE
para implementaciones de etiquetas NFC más genéricas a fin de admitir mejor los dispositivos con y sin Android.
Nota: Los URN para TNF_EXTERNAL_TYPE
tienen un formato canónico de urn:nfc:ext:example.com:externalType
. Sin embargo, la especificación de RTD de NFC Forum declara que la parte urn:nfc:ext:
del URN se debe omitir del registro NDEF. Por lo tanto, todo lo que debes proporcionar es el dominio (example.com
en el ejemplo) y el tipo (externalType
en el ejemplo) separados por dos puntos.
Cuando despacha TNF_EXTERNAL_TYPE
, Android convierte el URN urn:nfc:ext:example.com:externalType
en un URI vnd.android.nfc://ext/example.com:externalType
, que es lo que declara el filtro de intents del ejemplo.
Registros de aplicaciones de Android
Introducido en Android 4.0 (nivel de API 14), un registro de aplicaciones para Android (AAR) proporciona una certeza más sólida de que tu aplicación se inicia cuando se escanea una etiqueta NFC. Un AAR tiene el nombre del paquete de una aplicación incorporada dentro de un registro NDEF. Puedes agregar un AAR a cualquier registro NDEF de tu mensaje NDEF, porque Android busca AAR en todo el mensaje NDEF. Si encuentra un AAR, inicia la aplicación según el nombre del paquete dentro de este. Si la aplicación no está presente en el dispositivo, se iniciará Google Play para descargarla.
Los AAR son útiles si deseas evitar que otras aplicaciones filtren el mismo intent y, posiblemente, controlen etiquetas específicas que implementaste. Los AAR solo se admiten a nivel de la aplicación, debido a la restricción del nombre del paquete, y no a nivel de la actividad, como sucede con el filtrado de intents. Si deseas manejar un intent al nivel de la Actividad, usa filtros de intents.
Si una etiqueta contiene un AAR, se despacha el sistema de envío de etiquetas de la siguiente manera:
- Intenta iniciar una Actividad usando un filtro de intents como de costumbre. Si la actividad que coincide con el intent también coincide con el AAR, inicia la actividad.
- Si la actividad que filtra el intent no coincide con las AAR, si varias actividades pueden controlar el intent o si ninguna actividad lo hace, inicia la aplicación especificada por el AAR.
- Si ninguna aplicación puede comenzar con el AAR, ve a Google Play para descargar la aplicación basada en el AAR.
Nota: Puedes anular los AAR y el sistema de envío de intents con el sistema de envío en primer plano, que permite que una actividad en primer plano tenga prioridad cuando se descubre una etiqueta NFC. Con este método, la actividad debe estar en primer plano para anular los AAR y el sistema de envío de intents.
Si aún deseas filtrar las etiquetas analizadas que no contienen un AAR, puedes declarar los filtros de intents como normales. Esto es útil si tu aplicación está interesada en otras etiquetas que no contienen un AAR. Por ejemplo, es posible que desees garantizar que tu aplicación controle las etiquetas propias que implementas y las etiquetas generales implementadas por terceros. Ten en cuenta que los AAR son específicos para dispositivos con Android 4.0 o versiones posteriores, por lo que, cuando implementes etiquetas, lo más probable es que desees usar una combinación de AAR y tipos de MIME o URI para admitir la más amplia gama de dispositivos. Además, cuando implementes etiquetas NFC, piensa en cómo deseas escribirlas para habilitar la compatibilidad con la mayoría de los dispositivos (con Android y de otros tipos). Para ello, puedes definir un URI o tipo de MIME relativamente único de modo que las aplicaciones puedan distinguirlo con mayor facilidad.
Android proporciona una API simple para crear un AAR: createApplicationRecord()
. Todo lo que necesitas hacer es incorporar el AAR en cualquier parte de tu NdefMessage
. No es conveniente usar el primer registro de tu NdefMessage
, a menos que el AAR sea el único registro en NdefMessage
. Esto se debe a que el sistema Android verifica el primer registro de una NdefMessage
para determinar el tipo de MIME o URI de la etiqueta, que se usa con el fin de crear un intent para que las aplicaciones filtren. En el siguiente código, se muestra cómo crear un AAR:
Kotlin
val msg = NdefMessage( arrayOf( ..., NdefRecord.createApplicationRecord("com.example.android.beam") ) )
Java
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")} ); )
Cómo transmitir mensajes NDEF a otros dispositivos
Android Beam permite el intercambio de datos de igual a igual entre dos dispositivos con Android. La aplicación que desea transmitir datos a otro dispositivo debe estar en primer plano y el dispositivo que recibe los datos no debe estar bloqueado. Cuando el dispositivo de transmisión está lo suficientemente cerca con un dispositivo receptor, muestra la IU "Tocar para transmitir". Luego, el usuario puede elegir si desea transmitir el mensaje al dispositivo receptor.
Nota: La implementación de NDEF en primer plano estaba disponible en el nivel de API 10, que proporciona una funcionalidad similar a Android Beam. Estas APIs dejaron de estar disponibles, pero están disponibles para admitir dispositivos más antiguos. Consulta enableForegroundNdefPush()
para obtener más información.
Puedes habilitar Android Beam para tu aplicación llamando uno de los dos métodos:
setNdefPushMessage()
: Acepta unNdefMessage
para establecer como el mensaje que se transmitirá. Transmite automáticamente el mensaje cuando dos dispositivos están lo suficientemente cerca.setNdefPushMessageCallback()
: Acepta una devolución de llamada que contiene uncreateNdefMessage()
al que se llama cuando un dispositivo está dentro del rango para transmitir datos. La devolución de llamada te permite crear el mensaje NDEF solo cuando sea necesario.
Una actividad solo puede enviar un mensaje NDEF a la vez, por lo que setNdefPushMessageCallback()
tendrá prioridad sobre setNdefPushMessage()
si ambos están configurados. Para usar Android Beam, se deben cumplir los siguientes lineamientos generales:
- La actividad que transmite los datos debe estar en primer plano. Ambos dispositivos deben tener las pantallas desbloqueadas.
- Debes encapsular los datos que estás transmitiendo en un objeto
NdefMessage
. - El dispositivo NFC que recibe los datos transmitidos debe ser compatible con el protocolo push NDEF
com.android.npp
o el SNEP (Protocolo simple de intercambio NDEF) de NFC Forum. El protocolocom.android.npp
es obligatorio para los dispositivos con nivel de API 9 (Android 2.3) hasta API nivel 13 (Android 3.2).com.android.npp
y SNEP son obligatorios en el nivel de API 14 (Android 4.0) y versiones posteriores.
Nota: Si tu actividad habilita Android Beam y está en primer plano, el sistema estándar de envío de intents se inhabilita. Sin embargo, si tu actividad también habilita el envío en primer plano, puedes escanear etiquetas que coincidan con los filtros de intents establecidos en el envío en primer plano.
Para habilitar Android Beam, haz lo siguiente:
- Crea un
NdefMessage
que contenga lasNdefRecord
que deseas enviar al otro dispositivo. - Llama a
setNdefPushMessage()
con unNdefMessage
o asetNdefPushMessageCallback
pasando un objetoNfcAdapter.CreateNdefMessageCallback
en el métodoonCreate()
de tu actividad. Estos métodos requieren al menos una actividad que quieras habilitar con Android Beam, junto con una lista opcional de otras actividades para activar.En general, sueles usar
setNdefPushMessage()
si tu actividad solo necesita enviar el mismo mensaje NDEF en todo momento, cuando dos dispositivos están dentro del alcance para comunicarse. UsassetNdefPushMessageCallback
cuando tu aplicación se preocupa por el contexto actual de la aplicación y desea enviar un mensaje NDEF según lo que el usuario hace en tu aplicación.
En el siguiente ejemplo, se muestra cómo una actividad simple llama a NfcAdapter.CreateNdefMessageCallback
en el método onCreate()
de una actividad (consulta AndroidBeamDemo para ver la muestra completa). Este ejemplo también tiene métodos para ayudarte a crear un registro MIME:
Kotlin
package com.example.android.beam import android.app.Activity import android.content.Intent import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter import android.nfc.NfcAdapter.CreateNdefMessageCallback import android.nfc.NfcEvent import android.os.Bundle import android.os.Parcelable import android.widget.TextView import android.widget.Toast import java.nio.charset.Charset class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback { private var nfcAdapter: NfcAdapter? = null private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) textView = findViewById(R.id.textView) // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this) if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show() finish() return } // Register callback nfcAdapter?.setNdefPushMessageCallback(this, this) } override fun createNdefMessage(event: NfcEvent): NdefMessage { val text = "Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis() return NdefMessage( arrayOf( createMime("application/vnd.com.example.android.beam", text.toByteArray()) ) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. *///,NdefRecord.createApplicationRecord("com.example.android.beam") ) } override fun onResume() { super.onResume() // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { processIntent(intent) } } override fun onNewIntent(intent: Intent) { // onResume gets called after this to handle the intent setIntent(intent) } /** * Parses the NDEF Message from the intent and prints to the TextView */ private fun processIntent(intent: Intent) { textView = findViewById(R.id.textView) // only one message sent during the beam intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs -> (rawMsgs[0] as NdefMessage).apply { // record 0 contains the MIME type, record 1 is the AAR, if present textView.text = String(records[0].payload) } } } }
Java
package com.example.android.beam; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcAdapter.CreateNdefMessageCallback; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; import java.nio.charset.Charset; public class Beam extends Activity implements CreateNdefMessageCallback { NfcAdapter nfcAdapter; TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView textView = (TextView) findViewById(R.id.textView); // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show(); finish(); return; } // Register callback nfcAdapter.setNdefPushMessageCallback(this, this); } @Override public NdefMessage createNdefMessage(NfcEvent event) { String text = ("Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( new NdefRecord[] { createMime( "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. */ //,NdefRecord.createApplicationRecord("com.example.android.beam") }); return msg; } @Override public void onResume() { super.onResume(); // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } } @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent setIntent(intent); } /** * Parses the NDEF Message from the intent and prints to the TextView */ void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.textView); Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); // only one message sent during the beam NdefMessage msg = (NdefMessage) rawMsgs[0]; // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); } }
Ten en cuenta que este código comenta un AAR, que puedes quitar. Si habilitas el AAR, la aplicación especificada en él siempre recibe el mensaje de Android Beam. Si no está presente, se iniciará Google Play para descargarla. Por lo tanto, si se usa el AAR, el siguiente filtro de intents no es técnicamente necesario para los dispositivos con Android 4.0 o versiones posteriores:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter>
Con este filtro de intents, ahora se puede iniciar la aplicación com.example.android.beam
cuando escanea una etiqueta NFC o recibe un Android Beam con un AAR de tipo com.example.android.beam
, o cuando un mensaje con formato NDEF contiene un registro MIME de tipo application/vnd.com.example.android.beam
.
Si bien los AAR garantizan que se inicie o descargue una aplicación, se recomiendan los filtros de intents, ya que te permiten iniciar una actividad de tu elección en lugar de iniciar siempre la actividad principal dentro del paquete especificado por un AAR. Los AAR no tienen nivel de detalle de la actividad. Además, debido a que algunos dispositivos con Android no admiten AAR, también debes incorporar información de identificación en el primer registro NDEF de tus mensajes NDEF y filtrar eso también, por si acaso. Consulta Cómo crear tipos comunes de registros NDEF para obtener más información sobre cómo crear registros.