NFC를 이용한 모바일 결제가 서서히 인기를 끌고 있다. 그러나 인쇄에서 명함 정보 교환, 반복되는 작업 자동화
등 비즈니스 용도로 NFC를 활용할 방법은 놀랍도록 많다
원문보기:
http://www.ciokorea.com/slideshow/21259#csidx9d90e892fdd41b59a64bc8dd7902e0a
class NdefMessageParser
package org.androidtown.nfc;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import java.util.ArrayList;
import java.util.List;
public class NdefMessageParser {
// Utility class
private NdefMessageParser() {
}
public static List<ParsedRecord> parse(NdefMessage message) {
return getRecords(message.getRecords());
}
public static List<ParsedRecord> getRecords(NdefRecord[] records) {
List<ParsedRecord> elements = new ArrayList<ParsedRecord>();
for (NdefRecord record : records) {
if (UriRecord.isUri(record)) {
elements.add(UriRecord.parse(record));
} else if (TextRecord.isText(record)) {
elements.add(TextRecord.parse(record));
}
}
return elements;
}
}
class NFCPushActivity
package org.androidtown.nfc;
import java.nio.charset.Charset;
import java.util.Locale;
import android.app.Activity;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.widget.TextView;
public class NFCPushActivity extends Activity {
private NfcAdapter mAdapter;
private NdefMessage mMessage;
private TextView mText;
public static String sourceMsg = "Hello Push!";
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
mAdapter = NfcAdapter.getDefaultAdapter(this);
setContentView(R.layout.push);
mText = (TextView) findViewById(R.id.text);
if (mAdapter != null) {
mText.setText("푸쉬할 메시지 : " + sourceMsg + "\n\n다른 안드로이드폰의 NFC 앱을 터치하세요.");
} else {
mText.setText("먼저 NFC를 활성화하세요.");
}
mMessage = new NdefMessage(
new NdefRecord[] { createTextRecord(sourceMsg, Locale.ENGLISH, true)});
}
public void onResume() {
super.onResume();
if (mAdapter != null) mAdapter.enableForegroundNdefPush(this, mMessage);
}
public void onPause() {
super.onPause();
if (mAdapter != null) mAdapter.disableForegroundNdefPush(this);
}
public static NdefRecord createTextRecord(String text, 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 = text.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);
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
}
}
class NFCScanForegroundActivity
package org.androidtown.nfc;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.tech.NfcF;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import com.google.common.base.Charsets;
import com.google.common.primitives.Bytes;
/**
* NFC 태그의 값을 읽어들이는 방법에 대해 알 수 있습니다.
* 여기에서는 실제 태그를 읽어들일 수도 있지만 버튼을 눌러 가상 태그 데이터를 읽은 것처럼 만들 수도 있습니다.
*
* @author Mike
*/
public class NFCScanForegroundActivity extends Activity {
public static final String TAG = "NFCScanForegroundActivity";
public static final int REQ_CODE_PUSH = 1001;
public static final int SHOW_PUSH_CONFIRM = 2001;
private NfcAdapter mAdapter;
private PendingIntent mPendingIntent;
private IntentFilter[] mFilters;
private String[][] mTechLists;
private TextView mText;
private TextView broadcastBtn;
public static final int TYPE_TEXT = 1;
public static final int TYPE_URI = 2;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// NFC 어댑터 객체 참조
mAdapter = NfcAdapter.getDefaultAdapter(this);
setContentView(R.layout.scan);
Log.d(TAG, "onCreate() called.");
mText = (TextView) findViewById(R.id.text);
if (mAdapter == null) {
mText.setText("사용하기 전에 NFC를 활성화하세요.");
} else {
mText.setText("NFC 태그를 스캔하세요.");
}
// 버튼 이벤트 처리
broadcastBtn = (TextView) findViewById(R.id.broadcastBtn);
broadcastBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
int type = TYPE_TEXT;
String msg = "Hello Android!";
// 메모리에 태그 정보 생성
NdefMessage mMessage = createTagMessage(msg, type);
NdefMessage[] msgs = new NdefMessage[1];
msgs[0] = mMessage;
// 가상으로 인텐트 전달
Intent intent = new Intent(NfcAdapter.ACTION_TAG_DISCOVERED);
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, msgs);
startActivity(intent);
}
});
Intent targetIntent = new Intent(this, NFCScanForegroundActivity.class);
targetIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
mPendingIntent = PendingIntent.getActivity(this, 0, targetIntent, 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*");
} catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {
ndef,
};
mTechLists = new String[][] { new String[] { NfcF.class.getName() } };
Intent passedIntent = getIntent();
if (passedIntent != null) {
String action = passedIntent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
processTag(passedIntent);
}
}
}
public void onResume() {
super.onResume();
if (mAdapter != null) {
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
}
public void onPause() {
super.onPause();
if (mAdapter != null) {
mAdapter.disableForegroundDispatch(this);
}
}
private void processTag(Intent passedIntent) {
Log.d(TAG, "processTag() called.");
Parcelable[] rawMsgs = passedIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs == null) {
Log.d(TAG, "NDEF is null.");
return;
}
mText.setText(rawMsgs.length + "개 태그 스캔됨");
NdefMessage[] msgs;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
showTag(msgs[i]);
}
}
showDialog(SHOW_PUSH_CONFIRM);
}
/**
* 태그 정보 보여주기
*
* @param mMessage
* @return
*/
private int showTag(NdefMessage mMessage) {
List<ParsedRecord> records = NdefMessageParser.parse(mMessage);
final int size = records.size();
mText.append("\n");
for (int i = 0; i < size; i++) {
ParsedRecord record = records.get(i);
int recordType = record.getType();
String recordStr = "";
if (recordType == ParsedRecord.TYPE_TEXT) {
recordStr = "TEXT : " + ((TextRecord) record).getText() + "\n";
} else if (recordType == ParsedRecord.TYPE_URI) {
recordStr = "URI : " + ((UriRecord) record).getUri().toString() + "\n";
}
Log.d(TAG, "record string : " + recordStr);
mText.append(recordStr);
mText.invalidate();
}
return size;
}
public void onNewIntent(Intent passedIntent) {
Log.d(TAG, "onNewIntent() called.");
if (passedIntent != null) {
processTag(passedIntent);
}
}
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder;
switch (id) {
case SHOW_PUSH_CONFIRM:
builder = new AlertDialog.Builder(this);
builder.setTitle("푸쉬 액티비티");
builder.setMessage("푸쉬 액티비티를 띄우시겠습니까?");
builder.setPositiveButton("예",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent newIntent = new Intent(getApplicationContext(), NFCPushActivity.class);
startActivityForResult(newIntent, REQ_CODE_PUSH);
}
});
builder.setNegativeButton("아니오",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
return builder.create();
}
return null;
}
private NdefMessage createTagMessage(String msg, int type) {
NdefRecord[] records = new NdefRecord[1];
if (type == TYPE_TEXT) {
records[0] = createTextRecord(msg, Locale.KOREAN, true);
} else if (type == TYPE_URI){
records[0] = createUriRecord(msg.getBytes());
}
NdefMessage mMessage = new NdefMessage(records);
return mMessage;
}
private NdefRecord createTextRecord(String text, Locale locale, boolean encodeInUtf8) {
final byte[] langBytes = locale.getLanguage().getBytes(Charsets.US_ASCII);
final Charset utfEncoding = encodeInUtf8 ? Charsets.UTF_8 : Charset.forName("UTF-16");
final byte[] textBytes = text.getBytes(utfEncoding);
final int utfBit = encodeInUtf8 ? 0 : (1 << 7);
final char status = (char) (utfBit + langBytes.length);
final byte[] data = Bytes.concat(new byte[] {(byte) status}, langBytes, textBytes);
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
}
private NdefRecord createUriRecord(byte[] data) {
return new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI, new byte[0], data);
}
}
interface ParsedRecord
package org.androidtown.nfc;
public interface ParsedRecord {
public static final int TYPE_TEXT = 1;
public static final int TYPE_URI = 2;
public int getType();
}
class TextRecord
package org.androidtown.nfc;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import android.nfc.NdefRecord;
import com.google.common.base.Preconditions;
public class TextRecord implements ParsedRecord {
/** ISO/IANA language code */
private final String mLanguageCode;
private final String mText;
private TextRecord(String languageCode, String text) {
mLanguageCode = Preconditions.checkNotNull(languageCode);
mText = Preconditions.checkNotNull(text);
}
public int getType() {
return ParsedRecord.TYPE_TEXT;
}
public String getText() {
return mText;
}
/**
* Returns the ISO/IANA language code associated with this text element.
*/
public String getLanguageCode() {
return mLanguageCode;
}
// TODO: deal with text fields which span multiple NdefRecords
public static TextRecord parse(NdefRecord record) {
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT));
try {
byte[] payload = record.getPayload();
/*
* payload[0] contains the "Status Byte Encodings" field, per the
* NFC Forum "Text Record Type Definition" section 3.2.1.
*
* bit7 is the Text Encoding Field.
*
* if (Bit_7 == 0): The text is encoded in UTF-8 if (Bit_7 == 1):
* The text is encoded in UTF16
*
* Bit_6 is reserved for future use and must be set to zero.
*
* Bits 5 to 0 are the length of the IANA language code.
*/
String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0077;
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
String text =
new String(payload, languageCodeLength + 1,
payload.length - languageCodeLength - 1, textEncoding);
return new TextRecord(languageCode, text);
} catch (UnsupportedEncodingException e) {
// should never happen unless we get a malformed tag.
throw new IllegalArgumentException(e);
}
}
public static boolean isText(NdefRecord record) {
try {
parse(record);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
}
class UriRecord
package org.androidtown.nfc;
import java.nio.charset.Charset;
import java.util.Arrays;
import android.net.Uri;
import android.nfc.NdefRecord;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.primitives.Bytes;
public class UriRecord implements ParsedRecord {
private static final String TAG = "UriRecord";
public static final String RECORD_TYPE = "UriRecord";
/**
* NFC Forum "URI Record Type Definition"
*
* This is a mapping of "URI Identifier Codes" to URI string prefixes,
* per section 3.2.2 of the NFC Forum URI Record Type Definition document.
*/
private static final BiMap<Byte, String> URI_PREFIX_MAP = ImmutableBiMap.<Byte, String>builder()
.put((byte) 0x00, "")
.put((byte) 0x01, "http://www.")
.put((byte) 0x02, "https://www.")
.put((byte) 0x03, "http://")
.put((byte) 0x04, "https://")
.put((byte) 0x05, "tel:")
.put((byte) 0x06, "mailto:")
.put((byte) 0x07, "ftp://anonymous:anonymous@")
.put((byte) 0x08, "ftp://ftp.")
.put((byte) 0x09, "ftps://")
.put((byte) 0x0A, "sftp://")
.put((byte) 0x0B, "smb://")
.put((byte) 0x0C, "nfs://")
.put((byte) 0x0D, "ftp://")
.put((byte) 0x0E, "dav://")
.put((byte) 0x0F, "news:")
.put((byte) 0x10, "telnet://")
.put((byte) 0x11, "imap:")
.put((byte) 0x12, "rtsp://")
.put((byte) 0x13, "urn:")
.put((byte) 0x14, "pop:")
.put((byte) 0x15, "sip:")
.put((byte) 0x16, "sips:")
.put((byte) 0x17, "tftp:")
.put((byte) 0x18, "btspp://")
.put((byte) 0x19, "btl2cap://")
.put((byte) 0x1A, "btgoep://")
.put((byte) 0x1B, "tcpobex://")
.put((byte) 0x1C, "irdaobex://")
.put((byte) 0x1D, "file://")
.put((byte) 0x1E, "urn:epc:id:")
.put((byte) 0x1F, "urn:epc:tag:")
.put((byte) 0x20, "urn:epc:pat:")
.put((byte) 0x21, "urn:epc:raw:")
.put((byte) 0x22, "urn:epc:")
.put((byte) 0x23, "urn:nfc:")
.build();
private final Uri mUri;
private UriRecord(Uri uri) {
this.mUri = Preconditions.checkNotNull(uri);
}
public int getType() {
return ParsedRecord.TYPE_URI;
}
public Uri getUri() {
return mUri;
}
/**
* Convert {@link android.nfc.NdefRecord} into a {@link android.net.Uri}.
* This will handle both TNF_WELL_KNOWN / RTD_URI and TNF_ABSOLUTE_URI.
*
* @throws IllegalArgumentException if the NdefRecord is not a record
* containing a URI.
*/
public static UriRecord parse(NdefRecord record) {
short tnf = record.getTnf();
if (tnf == NdefRecord.TNF_WELL_KNOWN) {
return parseWellKnown(record);
} else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) {
return parseAbsolute(record);
}
throw new IllegalArgumentException("Unknown TNF " + tnf);
}
/** Parse and absolute URI record */
private static UriRecord parseAbsolute(NdefRecord record) {
byte[] payload = record.getPayload();
Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8")));
return new UriRecord(uri);
}
/** Parse an well known URI record */
private static UriRecord parseWellKnown(NdefRecord record) {
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_URI));
byte[] payload = record.getPayload();
/*
* payload[0] contains the URI Identifier Code, per the
* NFC Forum "URI Record Type Definition" section 3.2.2.
*
* payload[1]...payload[payload.length - 1] contains the rest of
* the URI.
*/
String prefix = URI_PREFIX_MAP.get(payload[0]);
byte[] fullUri =
Bytes.concat(prefix.getBytes(Charset.forName("UTF-8")), Arrays.copyOfRange(payload, 1,
payload.length));
Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));
return new UriRecord(uri);
}
public static boolean isUri(NdefRecord record) {
try {
parse(record);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
private static final byte[] EMPTY = new byte[0];
}
push.xml
<LinearLayout 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:orientation="vertical"
android:background="#ffffffff"
>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="#ff000000"
/>
</LinearLayout>
scan.xml
<LinearLayout 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:orientation="vertical"
android:background="#ffffffff"
>
<Button
android:id="@+id/broadcastBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="#ff000000"
android:text="태그 브로드캐스팅"
/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical|center_horizontal"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="#ff000000"
/>
</LinearLayout>
AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.androidtown.nfc" >
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".NFCScanForegroundActivity"
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.TAG_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="NFCPushActivity">
</activity>
</application>
</manifest>
133강
134강
댓글 ( 4)
댓글 남기기