Android - 學習操作NFC - 2

Android - 學習操作NFC - 2

在<Android - 學習操作NFC – 1>說明了Android在處理NFC tag的機制、tag dispatch system的運作流程,以及三種

ACTION_NDEF_DISCOVERED、ACTION_TECH_DISCOVERED與ACTION_TAG_DISCOVERED的處理方式與intent filter註冊方法。

該篇主要針對如何處理ACTION_NDEF_DISCOVERED的Reader、Writer進行說明。

 

首先說明如何撰寫常用的NDEF Records:

〉Creating Common Types of NDEF Records

     上一篇介紹簡單Reader的方式,該篇先由Write tag開始。因此,如果今天應用程式使用Android 4.0(API Level 14)使用createUri()

的方法幫助自動建立一個URI records;使用Android 4.1(API level 16)則可透過createExternal()createMime()幫助建立MIME與

external type的NDEF records。藉由使用這些方法以協助建立NDEF records。

 

以下便介紹操作NDEF message的第一個Record,來寫入資料至NFC tag或Beaming。

 

A. TNF_ABSOLUTE_URI

     建議使用RTD_URI類型取代TNF_ABSOLUTE_URI,因為RTD_URI是更有效的

‧Write:建立一個TNF_ABSOLUTE_URI的Ndef record:

// 建立個NdefRecord,指定type與payload
NdefRecord uriRecord = new NdefRecord(
    NdefRecord.TNF_ABSOLUTE_URI ,
    "http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
    new byte[0], 
    new byte[0]);

‧Read:定義intent filter取得Ndef record:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http"
        android:host="developer.android.com"
        android:pathPrefix="/index.html" />
</intent-filter>

註冊的Intent Filter只有在tag dispatch system偵測到對應的Ndef tag時才會觸發。

 

B. TNF_MIME_MEDIA

‧Write:

      (1) 建立一個TNF_MIME_MEDIA Ndef record:使用createMime()方法;(但僅在Android 4.1(API Level 16)以後才支援)

// 指定MIME類型,再將內容轉成byte[]
NdefRecord mimeRecord = NdefRecord.createMime(
            "application/vnd.com.example.android.beam",
            "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));

       (2) 建立一個TNF_MIME_MEDIA:使用NdefRecord物件:

// 指定Type、MIME Type與payload
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")));

‧Read:讀取MIME:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <!-- 定義要處理的MIME Type -->
    <data android:mimeType="application/vnd.com.example.android.beam" />
</intent-filter>

 

C. TNF_WELL_KNOWN with RTD_TEXT

‧Write:建立一個TNF_WELL_KNOWN的Ndef record:

public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
    // 取得預設的編碼格式
    byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
    // 準備轉換成UTF-8的編碼
    Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
 
    // 將內容依預設編碼轉成byte[]
    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);
 
    // 建立TNF_WELL_KNOWN的Ndef record
    NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
    NdefRecord.RTD_TEXT, new byte[0], data);
    return record;
}

‧Read:定義intent filter取得資料:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

 

D. TNF_WELL_KNOWN with RTD_URI

‧Write:建立TNF_WELL_KNOWN Ndef record,內容有RTD_URI;其方式與建立RTD_URI相似有分成二個:

(1) 建立URI有二個方式,一個由String –> URI;另一個是直接用URI物件,如下:

NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
 
// 上下為相同效果
Uri uri = new Uri("http://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
 
//add 1 for the URI Prefix
byte[] payload = new byte[uriField.length + 1];           
   
//prefixes http://www. to the URI
byte payload[0] = 0x01;    
                      
//appends URI to payload            
System.arraycopy(uriField, 0, payload, 1, uriField.length);  
 
NdefRecord rtdUriRecord = new NdefRecord(
    NdefRecord.TNF_WELL_KNOWN, 
    NdefRecord.RTD_URI, 
    new byte[0], 
    payload);

‧Read:定義要處理的intent filter:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http"
        android:host="example.com"
        android:pathPrefix="" />
</intent-filter>

 

E. TNF_EXTERNAL_TYPE

‧Write:使用createExternal()方法;或使用NdefRecord的方法手動建立;

//assign to your data
byte[] payload; 
//usually your app's package name
 
String domain = "com.example"; 
String type = "externalType";
 
// 指定domain, type, payload
NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
byte[] payload;
...
 
// 注意寫入的格式為: {domain}:{type}
NdefRecord extRecord = new NdefRecord(
    NdefRecord.TNF_EXTERNAL_TYPE, 
    "com.example:externalType", 
    new byte[0], 
    payload);

‧Read:註冊要處理的intent filter:

<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>

      使用TNF_EXTERNAL_TYPE是比較好用於一般的NFC tag,以支持Android或非Android系統可以讀取到這些Tag。

另外,要注意TNF_EXTERNAL_TYPE的URNs定義格式,如下:「urn:nfc:ext:example.com:externalType」;

根據NFC Forum RTD規格宣告[urn:nfc:ext]在某些Ndef message會被省略掉,因此,需要額外定義 domain (例如:example.com)與

type (例如:externalType)。當dispatching TNF_EXTERNAL_TYPE時,Android轉換 run:nfc:ext:example.com:externalType URN為

vnd.andorid.nfc://ext/example.com:externalType的URI,所以在定義intent filter時,在scheme為:vnd.android.nfc;host為ext;

patchPrefix為/example.com:externalType。

 

 

以上介紹了幾個常用Ndef message與Ndef record type的撰寫方式,需注意的是在Read的部分,在應用程式尚未被啟動時,

如果系統偵測到有Ndef Tag,它會發出對應的Intent,讓有註冊Filter intent接收到這個intent,進一步讓用戶選擇要執行的

應用程式。因此,如果應用程式本身支持多種不同的Filter intent均要記得加上去。

 

往下針對程式面說明要將上述的內容怎麼發佈給device或NFC tag。需要有那些重要的類別來加以完成。

參考<http://nfc.android.com/>中的<StickyNotes sample code>範例來加以說明:

 

步驟0-1:建立專案,指定使用<uses-sdk />要大於等於10,並且加入必要的<uses-permission />

<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.NFC"></uses-permission>

 

步驟0-2:標記預設應用程式要處理的Intent Filter

<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".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 -->
        <intent-filter>
            <!-- 註冊僅處理NDEF Tag,並指定預設啟動的Activity與處理的Type -->
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
    </activity>
 
</application>

註冊應用程式要處理的Intent Filter,此部分註冊的是應用程式非在前景模式時,如果系統有偵測到NDEF tag發出intent,

應用程式註冊了該intent filter則會被觸發到,如果有多個註冊相同的intent filter則會需要用戶進行選擇。

 

步驟1:處理應用程式註冊的Intent Filter,透過OnResume()事件進行處理將讀取的內容放置畫面的EditText中

1-1. 畫面配置

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
 
    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
 
        <Button
            android:id="@+id/write_tag"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Write to Tag" >
        </Button>
 
        <EditText
            android:id="@+id/note"
            android:layout_width="fill_parent"
            android:layout_height="388dp"
            android:gravity="top"
            android:text="Edit me." >
        </EditText>
    </LinearLayout>
 
</RelativeLayout>

       放置一個Button與EditText來顯示讀取到的內容,與負責寫入資料至NDEF Tag。

 

1-2. override處理OnResume()收到由系統送來的intent

@Override
protected void onResume()
{
    super.onResume();
 
    // 處理由Android系統送出應用程式處理的intent filter內容
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
        // 取得NdefMessage
        NdefMessage[] messages = getNdefMessages(getIntent());
        // 取得實際的內容
        byte[] payload = messages[0].getRecords()[0].getPayload();
        setNoteBody(new String(payload));
        // 往下送出該intent給其他的處理對象
        setIntent(new Intent()); 
    }
 
}

 

1-3. 拆解從Ndef Tag中取得的原始資料,並且轉換成NdefMessage內容

NdefMessage[] getNdefMessages(Intent intent) {
    // Parse the intent
    NdefMessage[] msgs = null;
    String action = intent.getAction();
    // 識別目前的action為何
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
            || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
        // 取得parcelabelarrry的資料
        Parcelable[] rawMsgs = 
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        // 取出的內容如果不為null,將parcelable轉成ndefmessage
        if (rawMsgs != null) {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        } else {
            // Unknown tag type
            byte[] empty = new byte[] {};
            NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
            NdefMessage msg = new NdefMessage(new NdefRecord[] {
                record
            });
            msgs = new NdefMessage[] {
                msg
            };
        }
    } else {
        Log.d(TAG, "Unknown intent.");
        finish();
    }
    return msgs;
}

 

1-4. 設得資料內容並回寫至畫面中

private void setNoteBody(String body) {
    Editable text = gNote.getText();
    text.clear();
    text.append(body);
}

 

1-5. 讓應用程式在前景模式下也能直接處理偵測到的Ndef Tdg,讓系統偵測到Ndef Tag時無需再重新啟動相同的應用程式

         為了讓應用程式在前景也可以處理intent filter,需要建立幾個必要的項目:

(1) 宣告PendingIntent:註冊讓應用程式的Activity負責處理所有接受到的NFC intents

// 註冊讓該Activity負責處理所有接收到的NFC Intents。
gNfcPendingIntent = PendingIntent.getActivity(
        this, 0,
        // 指定該Activity為應用程式中的最上層Activity
        new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

       在onCreate()中建立該PendingIntent,並且將gNfcPendingIntent宣告成全域變數。指定負責的Activity為最上層的Activity。

 

(2) 宣告IntentFilter[]:註冊要在前景處理的Intent Filter類型

// 建立要處理的Intent Filter負責處理來自Tag或p2p交換的資料。
IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
    ndefDetected.addDataType("text/plain");
} catch (MalformedMimeTypeException e) { }
 
gNdefExchangeFilters = new IntentFilter[] { ndefDetected };

      在onCreate()宣告要處理的IntentFilter,指定要處理的Data Type為MIME的文字,最後將IntentFilter加入全域變數的gNdefExchangeFilters。

 

(3) 覆寫onRsume()事件,讓Activity啟動時啟動NfcAdapter支持前景模式下處理NFC Intent

@Override
protected void onResume()
{
    super.onResume();
    gResumed = true;
    // 處理由Android系統送出應用程式處理的intent filter內容
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
        // 取得NdefMessage
        NdefMessage[] messages = getNdefMessages(getIntent());
        // 取得實際的內容
        byte[] payload = messages[0].getRecords()[0].getPayload();
        setNoteBody(new String(payload));
        // 往下送出該intent給其他的處理對象
        setIntent(new Intent()); 
    }
    // 啟動前景模式支持Nfc intent處理
    enableNdefExchangeMode();
}
 
/**
 * 啟動Ndef交換資料模式。
 */
private void enableNdefExchangeMode() {
    // 讓NfcAdapter啟動能夠在前景模式下進行intent filter的dispatch。
    gNfcAdapter.enableForegroundDispatch(
        this, gNfcPendingIntent, gNdefExchangeFilters, null);
}

      在onResume()中加入enableNdefExchangeMode()方法,裡面使用了「gNfcAdapter.enableForegroundDispatch()」方法,

      啟動NfcAdapter支持前景模式下處理NFC Intent。

 

(4) 覆寫onNewIntent()事件,補捉由其他應用程式或系統發出的Intent進行處理

@Override
protected void onNewIntent(Intent intent) {
    // 覆寫該Intent用於補捉如果有新的Intent進入時,可以觸發的事件任務。
    // NDEF exchange mode
    if (!gWriteMode && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        NdefMessage[] msgs = getNdefMessages(intent);
        promptForContent(msgs[0]);
    }
}
 
/**
 * 應用程式補捉到Ndef Message,詢問用戶是否要取代目前畫面中的文件。
 * @param msg
 */
private void promptForContent(final NdefMessage msg) {
    new AlertDialog.Builder(this).setTitle("Replace current content?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface arg0, int arg1) {
                String body = new String(msg.getRecords()[0].getPayload());
                setNoteBody(body);
            }
        })
        .setNegativeButton("No", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface arg0, int arg1) {
                
            }
        }).show();
}

          覆寫onNewIntent()事件以補捉當Activity收到系統送來的Intent時可以直接進行處理,不需要重新建立一個新的Activity處理。

          直接使用已存在的instance負責,另外增加promptForContent()方法來詢問用戶如果畫面中有資料是否要清除。

 

完成步驟0至步驟1,即可以完成讀取Ndef tag的功能。接下來步驟2要說明的是如何寫入text/plain的內容至Ndef tag;

 

步驟2:要讓App可以操作NFC,需要先取得NdefAdapter物件,才能啟動寫入資料至NFC tag或是其他應用程式;

2-1. 先針對畫面中按鈕、文字框二個控制項,並且分加入對應的Listener

@Override
protected void onCreate(Bundle savedInstanceState)
{
      // ...
      // 取得EditText與Button,並且註冊對應的事件
       findViewById(R.id.write_tag).setOnClickListener(this.gTagWriter);        
      gNote = (EditText)findViewById(R.id.note);
      gNote.addTextChangedListener(gTextWatcher);
      // ...
}

 

        a. Button註冊OnClickListener(),實作事件以啟動寫入資料至Tag或應用程式

private View.OnClickListener gTagWriter = new View.OnClickListener() {
    
    @Override
    public void onClick(View v)
    {
        // 先停止接收任何的Intent,準備寫入資料至tag;
        disableNdefExchangeMode();
        // 啟動寫入Tag模式,監測是否有Tag進入
        enableTagWriteMode();
        // 顯示對話框,告知將Tag或手機靠近本機的NFC感應區
        new AlertDialog.Builder(MainActivity.this)
                        .setTitle("Touch tag to write")
                        .setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog)
            {
                // 在取消模式下,先關閉監偵有Tag準備寫入的模式,再啟動等待資料交換的模式。
                // 停止寫入Tag模式,代表已有Tag進入
                disableTagWriteMode();
                // 啟動資料交換
                enableNdefExchangeMode();
            }
        }).create().show();
        
    }
};
 
/**
 * 啟動Ndef交換資料模式。
 */
private void enableNdefExchangeMode()
{
    // 讓NfcAdatper啟動前景Push資料至Tag或應用程式。
    gNfcAdapter.enableForegroundNdefPush(MainActivity.this, getNoteAsNdef());
    
    // 讓NfcAdapter啟動能夠在前景模式下進行intent filter的dispatch。
    gNfcAdapter.enableForegroundDispatch(this, gNfcPendingIntent, gNdefExchangeFilters, null);
}
 
private void disableNdefExchangeMode()
{
    gNfcAdapter.disableForegroundNdefPush(this);
    gNfcAdapter.disableForegroundDispatch(this);
}
 
/**
 * 啟動Tag寫入模式,註冊對應的Intent Filter來前景模式監聽是否有Tag進入的訊息。
 */
private void enableTagWriteMode()
{
    gWriteMode = true;
    IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
    gWriteTagFilters = new IntentFilter [] {tagDetected};
    gNfcAdapter.enableForegroundDispatch(this, gNfcPendingIntent, gWriteTagFilters, null);
}
 
/**
 * 停止Tag寫入模式,取消前景模式的監測。
 */
private void disableTagWriteMode()
{
    gWriteMode = false;
    gNfcAdapter.disableForegroundDispatch(this);
}

             該按鈕主要在點擊後,註冊對應的Intent Filter:ACTION_TAG_DISCOVERED,為了等待有Nfc Tag進入監偵範圍所觸發Intent事件,

             因此,搭配在onNewIntent()裡增加了寫識別intent.action進一步將EditText的內容寫入Tag中

             但在啟動另一個監偵時,記得先將預先監偵的ACTION_NDEF_DISCOVERED先取消,以免衝突。等到onCancel()事件啟動時,

             再取消ACTION_TAG_DISCOVERED的監偵,重新啟動ACTION_NDEF_DISCOVERED的監聽。

             =>另外需注意在enableNdefExchangedMode()裡,也啟動了前景模式發送資訊至應用程式。

        b. EditText註冊TextChangedListener(),宣告一個TextWatcher,處理在afterTextChanged()下啟動寫資料至應用程式

private TextWatcher gTextWatcher = new TextWatcher() {
 
    @Override
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
 
    }
 
    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
 
    }
 
    @Override
    public void afterTextChanged(Editable arg0) {
        // 如果是在Resume的狀態下,當編輯完後,啟動前景發佈訊息的功能。
        if (gResumed) {
            gNfcAdapter.enableForegroundNdefPush(MainActivity.this, getNoteAsNdef());
        }
    }
};

             在afterTextChanged()事件裡,先識別目前是否從onResume()進入,如果是將啟動NfcAdapter在前景模式推送訊息至其他App。

 

2-2. 增加onNewIntent()事件,處理當監偵到Ndef Tag時,寫入資料至Tag中

@Override
protected void onNewIntent(Intent intent)
{
    // 覆寫該Intent用於補捉如果有新的Intent進入時,可以觸發的事件任務。
    // NDEF exchange mode
    if (!gWriteMode && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        NdefMessage [] msgs = getNdefMessages(intent);
        promptForContent(msgs[0]);
    }
    
    // 監測到有指定ACTION進入,代表要寫入資料至Tag中。
    // Tag writing mode
    if (gWriteMode && NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        writeTag(getNoteAsNdef(), detectedTag);
    }
}

 

2-3. 寫入Ndef資料的方法

boolean writeTag(NdefMessage message, Tag tag) {
    int size = message.toByteArray().length;
 
    try {
        Ndef ndef = Ndef.get(tag);
        if (ndef != null) {
            ndef.connect();
 
            if (!ndef.isWritable()) {
                toast("Tag is read-only.");
                return false;
            }
            if (ndef.getMaxSize() < size) {
                toast("Tag capacity is " + ndef.getMaxSize() + " bytes, message is " + size
                        + " bytes.");
                return false;
            }
 
            ndef.writeNdefMessage(message);
            toast("Wrote message to pre-formatted tag.");
            return true;
        } else {
            NdefFormatable format = NdefFormatable.get(tag);
            if (format != null) {
                try {
                    format.connect();
                    format.format(message);
                    toast("Formatted tag and wrote message");
                    return true;
                } catch (IOException e) {
                    toast("Failed to format tag.");
                    return false;
                }
            } else {
                toast("Tag doesn't support NDEF.");
                return false;
            }
        }
    } catch (Exception e) {
        toast("Failed to write tag");
    }
 
    return false;
}
 
private void toast(String text) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}

        writeTag()方法增加了一些判斷的邏輯很值得參考。

 

2-4.  覆寫onPasue()事件,讓Activity暫停時關閉NfcAdapter的前景模式

@Override
protected void onPause()
{
    super.onPause();
    gResumed = false;
    // 由於NfcAdapter啟動前景模式將相對花費更多的電力,要記得關閉。
    gNfcAdapter.disableForegroundNdefPush(this);
}

 

[範例程式]

======

按照上方的說明應能實作出與NFC tag、應用程式交換資料的範例程式。該文章主要擷錄<StickyNotes sample code>的內容,

有更多相關的程式細節還有待補充與了解。如果有撰寫錯誤的地方,也請大家多多指教,謝謝。

 

References

NFC Demo - Android sample code

Near Field Communication (重要)

NFC Basics & Advanced NFC

Android NFC 开发教程(1):概述

Android NFC 开发教程(2): ApiDemos->NFC->ForegoundDispatch

Android NFC 開發教程(3): Mifare Tag 讀寫示例

NFC Programming in Android & [Android] 簡單範例: NFC Push

Developer Document & NXP TagWriter & StickyNotes sample code

onNewIntent调用时机 & onNewIntent的应用 & Activity - onNewIntent()

 

Dotblogs Tags: