Visão geral avançada da NFC

Este documento descreve tópicos avançados de NFC, como trabalhar com várias tecnologias de etiqueta, gravação em etiquetas NFC e envio em primeiro plano, o que permite que um aplicativo em primeiro plano processar intents mesmo quando outros aplicativos filtram as mesmas.

Trabalhar com tecnologias de etiqueta compatíveis

Ao trabalhar com etiquetas NFC e dispositivos com tecnologia Android, o principal formato usado para leitura e gravar dados nas tags é NDEF. Quando um dispositivo lê uma etiqueta com dados NDEF, o Android oferece suporte na análise da mensagem e na entrega dela em uma NdefMessage quando sempre que possível. No entanto, há casos em que você verifica uma tag que não contém Dados NDEF ou quando não foi possível mapear os dados NDEF para um tipo MIME ou URI. Nesses casos, você precisa abrir a comunicação diretamente com a tag e ler e gravar nela usando o seu próprio protocolo (em bytes brutos). O Android oferece suporte genérico para esses casos de uso com as android.nfc.tech, descrito na Tabela 1. Você pode usar o método getTechList() para determinar as tecnologias compatíveis com a tag e crie o parâmetro TagTechnology objeto com uma das classes fornecidas pelo 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 que foram formatadas como NDEF
NdefFormatable Fornece operações de formato para etiquetas que podem ser formatadas como NDEF.

As seguintes tecnologias de tag não precisam ser compatíveis com dispositivos 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 suporta MIFARE.
MifareUltralight Fornece acesso a propriedades MIFARE Ultralight e operações de E/S, se este dispositivo dispositivo suporta MIFARE.

Trabalhando com tecnologias de etiqueta e com o intent ACTION_TECH_DISCOVERED

Quando um dispositivo lê uma etiqueta que contém dados NDEF, mas que não pôde ser mapeada para um MIME ou URI, o sistema de expedição de tags tenta iniciar uma atividade com o ACTION_TECH_DISCOVERED. intenção. O ACTION_TECH_DISCOVERED também é usado quando uma tag com dados não NDEF são lidos. Com esse substituto, você pode trabalhar com os dados na tag diretamente se o sistema de expedição de etiquetas não puder analisá-lo para você. As etapas básicas para trabalhar com de tags são as seguintes:

  1. Filtre por uma intent ACTION_TECH_DISCOVERED especificando o tecnologias de tags que você quer usar. Consulte Como filtrar para NFC intents para mais informações. Em geral, o sistema de expedição de etiquetas tenta iniciar um intent ACTION_TECH_DISCOVERED quando uma mensagem NDEF não pode ser mapeada para um tipo MIME ou URI ou se a etiqueta lida não contiver dados NDEF. Para mais informações sobre como isso é determinado, consulte Sistema de expedição de etiquetas.
  2. Quando o app receber a intent, extraia o objeto Tag do a intent:

    Kotlin

    var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. Consiga uma instância de um TagTechnology chamando um dos Métodos de fábrica get das classes no pacote android.nfc.tech. Você pode Enumerar as tecnologias com suporte da tag chamando getTechList() antes de chamar um método de fábrica get. Por exemplo, para receber uma instância de MifareUltralight em um Tag, 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 tag NFC envolve obter a etiqueta da intenção e abrindo a comunicação com a tag. Você precisa definir sua própria pilha de protocolos para ler e gravar dados à tag. No entanto, lembre-se de que ainda é possível ler e gravar dados NDEF ao trabalhar com diretamente com uma tag. Cabe a você decidir como quer estruturar as coisas. A 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 uma intent e uma reivindicação prioridade sobre outras atividades que processam a mesma intent. O uso desse sistema envolve construir algumas estruturas de dados para que o sistema Android seja capaz de enviar as mensagens intents ao seu aplicativo. Para ativar o sistema de expedição em primeiro plano:

  1. Adicione o código abaixo ao método onCreate() da sua atividade:
    1. Criar um objeto PendingIntent mutável 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,
              PendingIntent.FLAG_MUTABLE)
      

      Java

      PendingIntent pendingIntent = PendingIntent.getActivity(
          this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
          PendingIntent.FLAG_MUTABLE);
      
    2. Declare os filtros de intent para processar os intents que você quer interceptar. Em primeiro plano o sistema de expedição verifica os filtros de intents especificados com a intenção recebida quando o dispositivo verifica a tag. Se corresponderem, o aplicativo processará o intent. Se isso acontecer, não correspondem, 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. que você quer filtrar para todas as tags substitutas para TAG_DISCOVERED intenção. O snippet de código abaixo processa todos os tipos MIME para NDEF_DISCOVERED. Você deve processar apenas aqueles de 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, };
      
    3. Configurar uma matriz de tecnologias de etiqueta que o aplicativo quer processar. Chame o método Object.class.getName() para extrair a classe da tecnologia que você que queremos apoiar.

      Kotlin

      techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
      

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. Substitua os seguintes callbacks do ciclo de vida da atividade e adicione lógica para ativar e desativar os envio em primeiro plano quando a atividade perde (onPause()) e recupera o foco (onResume()). enableForegroundDispatch() precisa ser chamado de na linha de execução principal e somente quando a atividade estiver em primeiro plano (chamar onResume() garante isso). Também é necessário implementar o callback onNewIntent para processar os dados da NFC lida. tag.
  3. 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
    }