Autore Topic: [facile] Lettura degli NFC  (Letto 9173 volte)

Offline maku85

  • Nuovo arrivato
  • *
  • Post: 28
  • Respect: +35
    • Mostra profilo
  • Dispositivo Android:
    Sony Xperia J
  • Play Store ID:
    MaKu
  • Sistema operativo:
    Windows 8.1
[facile] Lettura degli NFC
« il: 13 Settembre 2013, 14:40:07 CEST »
+1
Livello di difficoltà: facile
Min SDK: 10

Secondo Wikipedia
Citazione
NFC(Near Field Communication, in italiano letteralmente “Comunicazione in prossimità”) è una tecnologia che fornisce connettività wireless (RF) bidirezionale a corto raggio (fino a un massimo di 10 cm). Contrariamente ai più semplici dispositivi RFID, NFC permette una comunicazione bidirezionale: quando due apparecchi NFC (lo intiator e il target) vengono accostati entro un raggio di 4 cm, viene creata una rete peer-to-peer tra i due ed entrambi possono inviare e ricevere informazioni. La tecnologia NFC opera alla frequenza di 13,56 MHz e può raggiungere una velocità di trasmissione massima di 424 kbit/s. Lo NFC può essere realizzato direttamente tramite un chip integrato oppure tramite l'uso di una speciale scheda esterna che sfrutta le porte delle schede SD o micro SD.

In questo tutorial verrà mostrato come interrogare lo stato dell'NFC e come leggere i dati da un tag, verranno inoltre considerati solo gli NFC Data Exchange Format(NDEF) un formato definito da NFC Forum che può memorizzare e trasportare vari tipi di elementi. I dati NDEF sono incapsulati dentro un messaggio(NdefMessage) che contiente una o più righe (NdefRecord).Ogni record NDEF deve essere ben formato secondo le specifiche del tipo di record che si vuole creare. Un messaggio NDEF ben formato contiene nel primo record i seguenti campi:
  • 3-bit TNF (Type Name Format), che indica come interpretare il campo Variable length type
  • Variable length type, che descrive il tipo di campo
  • Variable length ID, che identifica univocamente il record
  • Variable length payload, il contenuto che si vuole leggere o scrivere. Un messaggio può contenere più records quindi non tutto il testo sarà contenuto nel primo record.
Come versione SDK minima verrà impostato il livello 10 perchè gli NFC sono supportati solo dalla versione Android 2.3.3 in poi.
Come primo esempio inseriremo nell'activity solo una textview.

Codice (XML): [Seleziona]
<TextView
   android:id="@+id/status"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content" />

Per accedere all'NFC hardware bisogna settare i permessi necessari nel file manifest.

Codice (XML): [Seleziona]
<uses-permission android:name="android.permission.NFC" />
<uses-feature
   android:name="android.hardware.nfc"
   android:required="true" />

L'activity che utilizzeremo per questo primo esempio provvederà semplicemente a verificare se l'NFC è attivo o meno. Per interagire con l'hardware si utilizza la classe NfcAdapter. Se l'NfcAdapter è nullo significa che il dispositivo non supporta gli NFC.

Codice (Java): [Seleziona]
public class MainActivity extends Activity {
    public static final String TAG = "NfcDemo";
    private TextView mTextView;
    private NfcAdapter mNfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.status);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter == null) {
            Toast.makeText(this, "Il dispositivo non supporta gli NFC.", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        if (!mNfcAdapter.isEnabled()) {
            mTextView.setText("NFC è disabilitato.");
        } else {
            mTextView.setText("NFC abilitato.");
        }
        handleIntent(getIntent());
    }

    private void handleIntent(Intent intent) {}
}

Ora provvederemo a ricevere una notifica dal sistema quando verrà avvicinato un tag NFC al dispositivo. Come succede di solito se ci sono più applicazioni installate sul dispositivo che supportano i propri Intent Android mostrerà un'activity per far scegliere all'utente quale applicazione avviare.
Ci sono diversi tipi di filtri per i tags:
  • ACTION_NDEF_DISCOVERED
  • ACTION_TECH_DISCOVERED
  • ACTION_TAG_DISCOVERED
La lista è ordinata per priorità decrescente(dal più alto al più basso).
Quando viene avvicinato un tag al dispositivo che supporta gli NDEF viene avviato l'Intent ACTION_NDEF_DISCOVERED. Se non ci sono Activity di altre applicazioni registrate per l'intent ACTION_NDEF_DISCOVERED o se il tag non supporta gli NDEF viene avviato un intent ACTION_TECH_DISCOVERED. Se non viene trovata nessuna applicazione neanche per l'intent ACTION_TECH_DISCOVERED o se il chip non riconosce la tecnologia viene attivato un intent ACTION_TAG_DISCOVERED.



Questo significa che ogni applicazione dovrebbe utilizzare il primo filtro(con priorità maggiore).

Proviamo prima ad utilizzare il secondo filtro per vedere la differenza. Bisogna prima specificare la tecnologia che ci interessa. In questo caso creiamo una sottocartella chiamata xml nella cartella res del nostro progetto. In questa cartella creiamo il file nfc_tech_filter.xml dove specificare la tecnologia(nel nostro caso è la tenologia NDEF).

Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
        <!-- class name -->
    </tech-list>
</resources>

Le altre tecnologie sono:

 
Codice (XML): [Seleziona]
       <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.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>

Adesso bisognerà creare un IntentFilter nel file manifest in modo che l'applicazione venga avviata appena si avvicina un tag al dispositivo.

Codice (xml
): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<activity
    android:name="android.nfc.demo.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <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>

Se non ci sono altre applicazioni registrate per questo Intent la nostra applicazione verrà avviata immediatamente altrimenti verrà mostrato il popup di scelta.

Come detto l'intent TECH_DISCOVERED ha la seconda priorità più alta ma se inseriamo l'intent NDEF_DISCOVERED e la nostra applicazione è l'unica a supportare la tecnologia NDEF, anche se ci sono altre applicazioni, verrà avviata direttamente la nostra.

Codice (XML): [Seleziona]
<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

Rimane però un problema. Se l'applicazione è già avviata e avviciniamo di nuovo il tag l'app si riaprirà nuovamente. Questo non è ciò che di solito si vuole. Per bypassare il problema useremo un Foreground Dispatch. Il sistema Foreground Dispatch permette ad un'activity di intercettare un intent e di richiederne la priorità sulle altre activity che gestiscono lo stesso intent.

Codice (Java): [Seleziona]
public class MainActivity extends Activity {
    public static final String MIME_TEXT_PLAIN = "text/plain";
    public static final String TAG = "NfcDemo";
    private TextView mTextView;
    private NfcAdapter mNfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.status);
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter == null) {
            Toast.makeText(this, "Il dispositivo non supporta gli NFC.", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        if (!mNfcAdapter.isEnabled()) {
            mTextView.setText("NFC è disabilitato.");
        } else {
            mTextView.setText("NFC abilitato.");
        }
        handleIntent(getIntent());
    }

    @Override
    protected void onResume() {
        super.onResume();
        setupForegroundDispatch(this, mNfcAdapter);
    }

    @Override
    protected void onPause() {
        stopForegroundDispatch(this, mNfcAdapter);
        super.onPause();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {}

    public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
        final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
        IntentFilter[] filters = new IntentFilter[1];
        String[][] techList = new String[][]{};
        filters[0] = new IntentFilter();
        filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
        filters[0].addCategory(Intent.CATEGORY_DEFAULT);
        try {
            filters[0].addDataType(MIME_TEXT_PLAIN);
        } catch (MalformedMimeTypeException e) {
            throw new RuntimeException("Check your mime type.");
        }
        adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
    }

    public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
        adapter.disableForegroundDispatch(activity);
    }
}

Praticamente sono stati inseriti nel metodo onResume e onPause l'avvio e lo stop del ForegroundDispatch e il metodo onNewIntent viene riavvicinato il tag al dispositivo.

Nell'handleIntent inseriremo la lettura dei dati dal tag.

Codice (Java): [Seleziona]
private void handleIntent(Intent intent) {
    String action = intent.getAction();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
        String type = intent.getType();
        if (MIME_TEXT_PLAIN.equals(type)) {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            new NdefReaderTask().execute(tag);
        } else {
            Log.d(TAG, "Mime type errato: " + type);
        }
    } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String[] techList = tag.getTechList();
        String searchedTech = Ndef.class.getName();
        for (String tech : techList) {
            if (searchedTech.equals(tech)) {
                new NdefReaderTask().execute(tag);
                break;
            }
        }
    }
}

Nell'esempio sopra sono stati previsti tutti e due gli intent(ACTION_NDEF_DISCOVERED e ACTION_TECH_DISCOVERED).
Il metodo NdefReaderTask serve per avviare un task in background per leggere il contenuto del tag.

Codice (Java): [Seleziona]
private class NdefReaderTask extends AsyncTask<Tag, Void, String> {
    @Override
    protected String doInBackground(Tag... params) {
        Tag tag = params[0];
        Ndef ndef = Ndef.get(tag);
        if (ndef == null) {
            return null;
        }
        NdefMessage ndefMessage = ndef.getCachedNdefMessage();
        NdefRecord[] records = ndefMessage.getRecords();
        for (NdefRecord ndefRecord : records) {
            if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
                try {
                    return readText(ndefRecord);
                } catch (UnsupportedEncodingException e) {
                    Log.e(TAG, "Encoding non supportato", e);
                }
            }
        }
        return null;
    }
    private String readText(NdefRecord record) throws UnsupportedEncodingException {
        byte[] payload = record.getPayload();
        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
        int languageCodeLength = payload[0] & 0063;
        // String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");

        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
    }
    @Override
    protected void onPostExecute(String result) {
        if (result != null) {
            mTextView.setText("Lettura contenuto: " + result);
        }
    }
}

Bibliografia:
« Ultima modifica: 13 Settembre 2013, 15:04:55 CEST da maku85 »
- Il programmatore è colui che risolve in un modo incomprensibile un problema che non sapevi di avere -

Offline Nicola_D

  • Moderatore
  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:[facile] Lettura degli NFC
« Risposta #1 il: 13 Settembre 2013, 15:13:07 CEST »
0
Ottimo tutorial, non sono arrivato a provarlo e credo che non arriverò ma leggendolo mi pare tutto perfetto!

Unica precisazione: I tag quando vengono comprati solitamente non sono NDEF, o meglio, non sono formattati. Vanno formattati NDEF per poi essere letti. Ovviamente questo non è sempre vero e quindi dipende dai casi, ma i tag che ho comprato io andavano prima formattati (ci sono tante app che lo fanno, NXP è uno degli sviluppatori più conosciuti in questo)
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Offline maku85

  • Nuovo arrivato
  • *
  • Post: 28
  • Respect: +35
    • Mostra profilo
  • Dispositivo Android:
    Sony Xperia J
  • Play Store ID:
    MaKu
  • Sistema operativo:
    Windows 8.1
Re:[facile] Lettura degli NFC
« Risposta #2 il: 13 Settembre 2013, 15:18:10 CEST »
0
Grazie per la precisazione, non ne ero al corrente. Non sono esperto di NFC ma ho pensato che il tutorial sarebbe potuto servire a qualcuno visto che ultimamente stanno diventando sempre più di moda.  :-)
- Il programmatore è colui che risolve in un modo incomprensibile un problema che non sapevi di avere -

Offline wlf

  • Utente normale
  • ***
  • Post: 362
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:[facile] Lettura degli NFC
« Risposta #3 il: 13 Maggio 2014, 10:33:32 CEST »
0
Salve,
sto provando con un TAG NXP MIFARE Classic. Ho utilizzato il codice dell'esempio ma la mia app non viene richiamata! :(
E' un problema relativo al tipo di TAG che sto utilizzando oppure è necessario utilizzare la ACTION_TAG_DISCOVERED?
Grazie.

Offline Nicola_D

  • Moderatore
  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:[facile] Lettura degli NFC
« Risposta #4 il: 13 Maggio 2014, 10:39:08 CEST »
0
Salve,
sto provando con un TAG NXP MIFARE Classic. Ho utilizzato il codice dell'esempio ma la mia app non viene richiamata! :(
E' un problema relativo al tipo di TAG che sto utilizzando oppure è necessario utilizzare la ACTION_TAG_DISCOVERED?
Grazie.
Che device hai? purtroppo negli ultimi device il mifare Classic non è piu supportato perchè se non hai il chip NXP non te li legge (ad esempio Nexus 4 e 5 hanno chip broadcomm).
Usa l'app NXP Tag Info per controllare se Mifare Classic è supportato dal tuo dispositivo.
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Offline wlf

  • Utente normale
  • ***
  • Post: 362
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:[facile] Lettura degli NFC
« Risposta #5 il: 14 Maggio 2014, 12:41:55 CEST »
0
Che device hai?

Ho provato con più di un dispositivo, ma quello "ufficiale" sarebbe un XPERIA.

Citazione
purtroppo negli ultimi device il mifare Classic non è piu supportato perchè se non hai il chip NXP non te li legge (ad esempio Nexus 4 e 5 hanno chip broadcomm). Usa l'app NXP Tag Info per controllare se Mifare Classic è supportato dal tuo dispositivo.

Non ho dubbi in questo senso visto che non mi apre la mia app ma comunque fa partire un'altra app che gestisce gli NFC; io mi sarei atteso che mi facesse scegliere tra le due! :(

Correggetemi se sbaglio, ma l'esempio del tutorial prevede che l'App realizzata si renda disponibile a gestire un broacast message della periferica NFC. Questo presuppone che l'app sia installata sul device dopo di che questa rendendosi disponibile a gestire i contenuti NFC viene invocata dal sistema e questa può leggere il contenuto del TAG entrando nel merito.

Io veramente stavo cercando un esempio meno complesso; come di certo saprete è possibile definire sui TAG un record con una "Application" specifica da far partire; mi interesserebbe aggiungere un parametro a questo lancio, ad esempio un App generica a cui passare una stringa con un utente e una password. Riesco a far partire l'App, ma alla partenza non riesco a leggere il secondo record ed eventualmente anche un terzo, riavvicinando il TAG riesco a leggere tutti i record memorizzati una volta partita. Io vorrei avvicinare il TAG una sola volta per ottenere la partenza dell'App e la lettura dei parametri ... :(

Offline squalo92

  • Nuovo arrivato
  • *
  • Post: 1
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    lg g3
  • Sistema operativo:
    windows 10
Re:[facile] Lettura degli NFC
« Risposta #6 il: 22 Settembre 2016, 12:20:33 CEST »
0
Salve ragazzi, io vorrei che l'app legga il tag solo quando voglio io e non quando passo il tag. Insomma vorrei che quando clikko su un button allora io devo passare il tag e me lo deve leggere.
grazie

Offline wlf

  • Utente normale
  • ***
  • Post: 362
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:[facile] Lettura degli NFC
« Risposta #7 il: 22 Settembre 2016, 18:32:35 CEST »
0
Se hai la funzionalità NFC attiva nelle impostazioni dello smartphone non puoi inibire che il TAG venga letto. Dovresti quindi controllare che l'NFC sia disattivato ... altrimenti viene letto.

Potresti comunque far si che anche se viene letto e senti la notifica con il suono e la vibrazione che poi dalla tua App non facci niente ... sempre che nel TAG non ci sia la partenza di un App, in questo caso è il SO che lancia l'App ...