Este documento descreve tópicos avançados de NFC, como trabalhar com várias tecnologias de etiqueta, gravação em etiquetas NFC e expedição em primeiro plano que permite que um aplicativo em primeiro plano processe intents mesmo quando outros aplicativos aplicam filtros para os mesmos intents.
Trabalhar com tecnologias de etiqueta compatíveis
Ao trabalhar com etiquetas NFC e dispositivos com tecnologia Android, o formato principal usado para ler e gravar dados em etiquetas é o NDEF. Quando um dispositivo faz a leitura de uma etiqueta com dados NDEF, o Android fornece suporte para analisar a mensagem e exibi-la em um NdefMessage
, quando possível. No entanto, há casos em que é feita a leitura de uma etiqueta que não contém dados NDEF ou quando os dados NDEF não puderam ser mapeados para um tipo MIME ou URI.
Nesses casos, é preciso abrir a comunicação diretamente com a etiqueta e ler e gravar nela usando um protocolo próprio (em bytes brutos). O Android fornece compatibilidade genérica nesses casos de uso com o pacote android.nfc.tech
, que é descrito na Tabela 1. O getTechList()
pode ser usado para determinar as tecnologias compatíveis com a etiqueta e criar o objeto TagTechnology
correspondente com uma das classes fornecidas por android.nfc.tech
Tabela 1. Tecnologias de etiqueta compatíveis
Classe | Descrição |
---|---|
TagTechnology |
A interface que todas as classes de tecnologia de etiquetas precisam implementar. |
NfcA |
Fornece acesso às propriedades NFC-A (ISO 14443-3A) e operações de E/S. |
NfcB |
Fornece acesso às propriedades NFC-B (ISO 14443-3B) e operações de E/S. |
NfcF |
Fornece acesso às propriedades da NFC-F (JIS 6319-4) e operações de E/S. |
NfcV |
Fornece acesso às propriedades NFC-V (ISO 15693) e operações de E/S. |
IsoDep |
Fornece acesso às propriedades ISO-DEP (ISO 14443-4) e operações de E/S. |
Ndef |
Fornece acesso a dados e operações NDEF em etiquetas NFC formatadas como NDEF. |
NdefFormatable |
Fornece operações de formato para etiquetas que podem ser formatadas como NDEF. |
As tecnologias de etiqueta a seguir não precisam ser compatíveis com dispositivos com tecnologia Android.
Tabela 2. Tecnologias de etiqueta compatíveis opcionais
Classe | Descrição |
---|---|
MifareClassic |
Fornece acesso a propriedades MIFARE Classic e operações de E/S, se este dispositivo Android for compatível com MIFARE. |
MifareUltralight |
Fornece acesso a propriedades MIFARE Ultralight e operações de E/S, se este dispositivo Android for compatível com MIFARE. |
Trabalhando com tecnologias de etiqueta e com o intent ACTION_TECH_DISCOVERED
Quando um dispositivo faz a leitura de uma etiqueta que contém dados NDEF, mas não pôde ser mapeada para um MIME ou URI, o sistema de expedição de etiquetas tenta iniciar uma atividade com o intent ACTION_TECH_DISCOVERED
. O ACTION_TECH_DISCOVERED
também é usado quando uma etiqueta com dados não formatos como-NDEF é lida. Esse substituto permite trabalhar diretamente com os dados da etiqueta se o sistema de expedição de etiquetas não puder analisá-los. As etapas básicas ao trabalhar com tecnologias de etiquetas são as seguintes:
- Aplique um filtro para um intent
ACTION_TECH_DISCOVERED
que especifique as tecnologias de etiqueta que você quer processar. Consulte Como aplicar filtros para intents NFC para ver mais informações. Em geral, o sistema de expedição de etiquetas tenta iniciar um intentACTION_TECH_DISCOVERED
quando uma mensagem NDEF não pode ser mapeada para um tipo MIME ou URI, ou se a etiqueta lida não continha dados NDEF. Para mais informações sobre como isso é determinado, consulte Sistema de expedição de etiquetas . - Quando o aplicativo receber o intent, pegue o objeto
Tag
do intent:Kotlin
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- Uma instância de
TagTechnology
é obtida, chamando um dos métodos de fábricaget
das classes no pacoteandroid.nfc.tech
. Tecnologias compatíveis com a etiqueta podem ser enumeradas chamandogetTechList()
antes de chamar um método de fábricaget
. Por exemplo, para obter uma instância deMifareUltralight
de umTag
, faça o seguinte:Kotlin
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
Java
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
Ler e gravar em etiquetas
Ler e gravar em uma etiqueta NFC envolve obter a etiqueta do intent e abrir a comunicação com a etiqueta. Uma pilha de protocolos própria precisa ser definida para ler e gravar dados na etiqueta. No entanto, lembre-se que é possível ler e gravar dados NDEF ao trabalhar diretamente com uma etiqueta. Cabe a você decidir como quer estruturar as coisas. O exemplo a seguir mostra como trabalhar com uma etiqueta MIFARE Ultralight.
Kotlin
package com.example.android.nfc import android.nfc.Tag import android.nfc.tech.MifareUltralight import java.io.IOException import java.nio.charset.Charset class MifareUltralightTagTester { fun writeTag(tag: Tag, tagText: String) { MifareUltralight.get(tag)?.use { ultralight -> ultralight.connect() Charset.forName("US-ASCII").also { usAscii -> ultralight.writePage(4, "abcd".toByteArray(usAscii)) ultralight.writePage(5, "efgh".toByteArray(usAscii)) ultralight.writePage(6, "ijkl".toByteArray(usAscii)) ultralight.writePage(7, "mnop".toByteArray(usAscii)) } } fun readTag(tag: Tag): String? { return MifareUltralight.get(tag)?.use { mifare -> mifare.connect() val payload = mifare.readPages(4) String(payload, Charset.forName("US-ASCII")) } } } }
Java
package com.example.android.nfc; import android.nfc.Tag; import android.nfc.tech.MifareUltralight; import android.util.Log; import java.io.IOException; import java.nio.charset.Charset; public class MifareUltralightTagTester { private static final String TAG = MifareUltralightTagTester.class.getSimpleName(); public void writeTag(Tag tag, String tagText) { MifareUltralight ultralight = MifareUltralight.get(tag); try { ultralight.connect(); ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII"))); } catch (IOException e) { Log.e(TAG, "IOException while writing MifareUltralight...", e); } finally { try { ultralight.close(); } catch (IOException e) { Log.e(TAG, "IOException while closing MifareUltralight...", e); } } } public String readTag(Tag tag) { MifareUltralight mifare = MifareUltralight.get(tag); try { mifare.connect(); byte[] payload = mifare.readPages(4); return new String(payload, Charset.forName("US-ASCII")); } catch (IOException e) { Log.e(TAG, "IOException while reading MifareUltralight message...", e); } finally { if (mifare != null) { try { mifare.close(); } catch (IOException e) { Log.e(TAG, "Error closing tag...", e); } } } return null; } }
Usar o sistema de expedição em primeiro plano
O sistema de expedição em primeiro plano permite que uma atividade intercepte um intent e reivindique a prioridade sobre outras atividades que processam o mesmo intent. Usar esse sistema envolve a construção de algumas estruturas de dados para que o sistema Android possa enviar os intents apropriados para o aplicativo. Para ativar o sistema de expedição em primeiro plano:
- Adicione o código a seguir no método
onCreate()
da atividade:- .
- Crie um objeto
PendingIntent
para que o sistema Android possa preenchê-lo com os detalhes da etiqueta quando ela for lida.Kotlin
val intent = Intent(this, javaClass).apply { addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
Java
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
- Declare os filtros de intent para processar os intents que você quer interceptar. O sistema de expedição em primeiro plano verifica os filtros de intent especificados com o intent recebido quando o dispositivo faz a leitura de uma etiqueta. Se corresponderem, o aplicativo processará o intent. Se não houver correspondência, o sistema de expedição em primeiro plano volta para o sistema de expedição de intents.
Especificar uma matriz
null
de filtros de intent e de tecnologia significa que você quer aplicar um filtro para todas as etiquetas que usam o intentTAG_DISCOVERED
como substituto. O snippet de código abaixo processa todos os tipos MIME paraNDEF_DISCOVERED
. Recomenda-se processar apenas os que você precisa.Kotlin
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { try { addDataType("*/*") /* Handles all MIME based dispatches. You should specify only the ones that you need. */ } catch (e: IntentFilter.MalformedMimeTypeException) { throw RuntimeException("fail", e) } } intentFiltersArray = arrayOf(ndef)
Java
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try { ndef.addDataType("*/*"); /* Handles all MIME based dispatches. You should specify only the ones that you need. */ } catch (MalformedMimeTypeException e) { throw new RuntimeException("fail", e); } intentFiltersArray = new IntentFilter[] {ndef, };
- Configurar uma matriz de tecnologias de etiqueta que o aplicativo quer processar. Chame o método
Object.class.getName()
para obter a classe da tecnologia à qual quer oferecer compatibilidade.Kotlin
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Java
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- Crie um objeto
- Modifique os seguintes callbacks do ciclo de vida da atividade e adicione lógica para ativar e desativar a expedição em primeiro plano quando a atividade perder (
onPause()
) e recuperar o foco (onResume()
).enableForegroundDispatch()
precisa ser chamado na linha de execução principal e apenas quando a atividade estiver em primeiro plano (chamaronResume()
garante isso). Também é necessário implementar o callbackonNewIntent
para processar os dados da etiqueta NFC lida.
Kotlin
public override fun onPause() { super.onPause() adapter.disableForegroundDispatch(this) } public override fun onResume() { super.onResume() adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray) } public override fun onNewIntent(intent: Intent) { val tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) //do something with tagFromIntent }
Java
public void onPause() { super.onPause(); adapter.disableForegroundDispatch(this); } public void onResume() { super.onResume(); adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray); } public void onNewIntent(Intent intent) { Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //do something with tagFromIntent }
Veja o exemplo ForegroundDispatch nas demonstrações da API para a demonstração completa.