NFC sample app

Cherry-picking NFC sample into Honeycomb branch, since it wasn't
merged during it's original commit to Gingerbread.

Change-Id: I79308f04c1b2edd6cb5bb675fa13b9e5083a8432
This commit is contained in:
Megha Joshi
2010-11-02 23:27:24 -07:00
committed by Trevor Johns
parent d24e1aeaa6
commit 6aa608a559
17 changed files with 1172 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.nfc;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import com.example.android.nfc.record.ParsedNdefRecord;
import com.example.android.nfc.record.SmartPoster;
import com.example.android.nfc.record.TextRecord;
import com.example.android.nfc.record.UriRecord;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class for creating {@link ParsedNdefMessage}s.
*/
public class NdefMessageParser {
// Utility class
private NdefMessageParser() {
}
/** Parse an NdefMessage */
public static List<ParsedNdefRecord> parse(NdefMessage message) {
return getRecords(message.getRecords());
}
public static List<ParsedNdefRecord> getRecords(NdefRecord[] records) {
List<ParsedNdefRecord> elements = new ArrayList<ParsedNdefRecord>();
for (NdefRecord record : records) {
if (UriRecord.isUri(record)) {
elements.add(UriRecord.parse(record));
} else if (TextRecord.isText(record)) {
elements.add(TextRecord.parse(record));
} else if (SmartPoster.isPoster(record)) {
elements.add(SmartPoster.parse(record));
}
}
return elements;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.nfc;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.android.nfc.record.ParsedNdefRecord;
import java.util.List;
/**
* An {@link Activity} which handles a broadcast of a new tag that the device
* just discovered.
*/
public class TagViewer extends Activity {
static final String TAG = "ViewTag";
/**
* This activity will finish itself in this amount of time if the user
* doesn't do anything.
*/
static final int ACTIVITY_TIMEOUT_MS = 1 * 1000;
TextView mTitle;
LinearLayout mTagContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tag_viewer);
mTagContent = (LinearLayout) findViewById(R.id.list);
mTitle = (TextView) findViewById(R.id.title);
resolveIntent(getIntent());
}
void resolveIntent(Intent intent) {
// Parse the intent
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
// When a tag is discovered we send it to the service to be save. We
// include a PendingIntent for the service to call back onto. This
// will cause this activity to be restarted with onNewIntent(). At
// that time we read it from the database and view it.
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs;
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};
}
// Setup the views
setTitle(R.string.title_scanned_tag);
buildTagViews(msgs);
} else {
Log.e(TAG, "Unknown intent " + intent);
finish();
return;
}
}
void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) {
return;
}
LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout content = mTagContent;
// Clear out any old views in the content area, for example if you scan
// two tags in a row.
content.removeAllViews();
// Parse the first message in the list
// Build views for all of the sub records
List<ParsedNdefRecord> records = NdefMessageParser.parse(msgs[0]);
final int size = records.size();
for (int i = 0; i < size; i++) {
ParsedNdefRecord record = records.get(i);
content.addView(record.getView(this, inflater, content, i));
inflater.inflate(R.layout.tag_divider, content, true);
}
}
@Override
public void onNewIntent(Intent intent) {
setIntent(intent);
resolveIntent(intent);
}
@Override
public void setTitle(CharSequence title) {
mTitle.setText(title);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.nfc.record;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public interface ParsedNdefRecord {
/**
* Returns a view to display this record.
*/
public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent,
int offset);
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.nfc.record;
import android.app.Activity;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.example.android.nfc.NdefMessageParser;
import com.example.android.nfc.R;
import java.util.Arrays;
import java.util.NoSuchElementException;
/**
* A representation of an NFC Forum "Smart Poster".
*/
public class SmartPoster implements ParsedNdefRecord {
/**
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
*
* "The Title record for the service (there can be many of these in
* different languages, but a language MUST NOT be repeated). This record is
* optional."
*/
private final TextRecord mTitleRecord;
/**
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
*
* "The URI record. This is the core of the Smart Poster, and all other
* records are just metadata about this record. There MUST be one URI record
* and there MUST NOT be more than one."
*/
private final UriRecord mUriRecord;
/**
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
*
* "The Action record. This record describes how the service should be
* treated. For example, the action may indicate that the device should save
* the URI as a bookmark or open a browser. The Action record is optional.
* If it does not exist, the device may decide what to do with the service.
* If the action record exists, it should be treated as a strong suggestion;
* the UI designer may ignore it, but doing so will induce a different user
* experience from device to device."
*/
private final RecommendedAction mAction;
/**
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
*
* "The Type record. If the URI references an external entity (e.g., via a
* URL), the Type record may be used to declare the MIME type of the entity.
* This can be used to tell the mobile device what kind of an object it can
* expect before it opens the connection. The Type record is optional."
*/
private final String mType;
private SmartPoster(UriRecord uri, TextRecord title, RecommendedAction action, String type) {
mUriRecord = Preconditions.checkNotNull(uri);
mTitleRecord = title;
mAction = Preconditions.checkNotNull(action);
mType = type;
}
public UriRecord getUriRecord() {
return mUriRecord;
}
/**
* Returns the title of the smart poster. This may be {@code null}.
*/
public TextRecord getTitle() {
return mTitleRecord;
}
public static SmartPoster parse(NdefRecord record) {
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
try {
NdefMessage subRecords = new NdefMessage(record.getPayload());
return parse(subRecords.getRecords());
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
public static SmartPoster parse(NdefRecord[] recordsRaw) {
try {
Iterable<ParsedNdefRecord> records = NdefMessageParser.getRecords(recordsRaw);
UriRecord uri = Iterables.getOnlyElement(Iterables.filter(records, UriRecord.class));
TextRecord title = getFirstIfExists(records, TextRecord.class);
RecommendedAction action = parseRecommendedAction(recordsRaw);
String type = parseType(recordsRaw);
return new SmartPoster(uri, title, action, type);
} catch (NoSuchElementException e) {
throw new IllegalArgumentException(e);
}
}
public static boolean isPoster(NdefRecord record) {
try {
parse(record);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
if (mTitleRecord != null) {
// Build a container to hold the title and the URI
LinearLayout container = new LinearLayout(activity);
container.setOrientation(LinearLayout.VERTICAL);
container.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
container.addView(mTitleRecord.getView(activity, inflater, container, offset));
inflater.inflate(R.layout.tag_divider, container);
container.addView(mUriRecord.getView(activity, inflater, container, offset));
return container;
} else {
// Just a URI, return a view for it directly
return mUriRecord.getView(activity, inflater, parent, offset);
}
}
/**
* Returns the first element of {@code elements} which is an instance of
* {@code type}, or {@code null} if no such element exists.
*/
private static <T> T getFirstIfExists(Iterable<?> elements, Class<T> type) {
Iterable<T> filtered = Iterables.filter(elements, type);
T instance = null;
if (!Iterables.isEmpty(filtered)) {
instance = Iterables.get(filtered, 0);
}
return instance;
}
private enum RecommendedAction {
UNKNOWN((byte) -1), DO_ACTION((byte) 0), SAVE_FOR_LATER((byte) 1), OPEN_FOR_EDITING(
(byte) 2);
private static final ImmutableMap<Byte, RecommendedAction> LOOKUP;
static {
ImmutableMap.Builder<Byte, RecommendedAction> builder = ImmutableMap.builder();
for (RecommendedAction action : RecommendedAction.values()) {
builder.put(action.getByte(), action);
}
LOOKUP = builder.build();
}
private final byte mAction;
private RecommendedAction(byte val) {
this.mAction = val;
}
private byte getByte() {
return mAction;
}
}
private static NdefRecord getByType(byte[] type, NdefRecord[] records) {
for (NdefRecord record : records) {
if (Arrays.equals(type, record.getType())) {
return record;
}
}
return null;
}
private static final byte[] ACTION_RECORD_TYPE = new byte[] {'a', 'c', 't'};
private static RecommendedAction parseRecommendedAction(NdefRecord[] records) {
NdefRecord record = getByType(ACTION_RECORD_TYPE, records);
if (record == null) {
return RecommendedAction.UNKNOWN;
}
byte action = record.getPayload()[0];
if (RecommendedAction.LOOKUP.containsKey(action)) {
return RecommendedAction.LOOKUP.get(action);
}
return RecommendedAction.UNKNOWN;
}
private static final byte[] TYPE_TYPE = new byte[] {'t'};
private static String parseType(NdefRecord[] records) {
NdefRecord type = getByType(TYPE_TYPE, records);
if (type == null) {
return null;
}
return new String(type.getPayload(), Charsets.UTF_8);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.nfc.record;
import android.app.Activity;
import android.nfc.NdefRecord;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.common.base.Preconditions;
import com.example.android.nfc.R;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
/**
* An NFC Text Record
*/
public class TextRecord implements ParsedNdefRecord {
/** 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 View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
text.setText(mText);
return 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;
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.nfc.record;
import android.app.Activity;
import android.net.Uri;
import android.nfc.NdefRecord;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.primitives.Bytes;
import com.example.android.nfc.R;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* A parsed record containing a Uri.
*/
public class UriRecord implements ParsedNdefRecord {
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 View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
text.setText(mUri.toString());
return text;
}
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];
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.example.android.nfc.simulator;
import android.app.ListActivity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import java.nio.charset.Charset;
import java.util.Locale;
/**
* A activity that launches tags as if they had been scanned.
*/
public class FakeTagsActivity extends ListActivity {
static final String TAG = "FakeTagsActivity";
static final byte[] UID = new byte[] {0x05, 0x00, 0x03, 0x08};
ArrayAdapter<TagDescription> mAdapter;
public static NdefRecord newTextRecord(String text, Locale locale, boolean encodeInUtf8) {
Preconditions.checkNotNull(text);
Preconditions.checkNotNull(locale);
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);
}
public static NdefRecord newMimeRecord(String type, byte[] data) {
Preconditions.checkNotNull(type);
Preconditions.checkNotNull(data);
final byte[] typeBytes = type.getBytes(Charsets.US_ASCII);
return new NdefRecord(NdefRecord.TNF_MIME_MEDIA, typeBytes, new byte[0], data);
}
static final class TagDescription {
public String title;
public NdefMessage[] msgs;
public TagDescription(String title, byte[] bytes) {
this.title = title;
try {
msgs = new NdefMessage[] {new NdefMessage(bytes)};
} catch (final Exception e) {
throw new RuntimeException("Failed to create tag description", e);
}
}
@Override
public String toString() {
return title;
}
}
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
final ArrayAdapter<TagDescription> adapter = new ArrayAdapter<TagDescription>(
this, android.R.layout.simple_list_item_1, android.R.id.text1);
adapter.add(
new TagDescription("Broadcast NFC Text Tag", MockNdefMessages.ENGLISH_PLAIN_TEXT));
adapter.add(new TagDescription(
"Broadcast NFC SmartPoster URL & text", MockNdefMessages.SMART_POSTER_URL_AND_TEXT));
adapter.add(new TagDescription(
"Broadcast NFC SmartPoster URL", MockNdefMessages.SMART_POSTER_URL_NO_TEXT));
setListAdapter(adapter);
mAdapter = adapter;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
final TagDescription description = mAdapter.getItem(position);
final Intent intent = new Intent(NfcAdapter.ACTION_TAG_DISCOVERED);
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, description.msgs);
startActivity(intent);
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.nfc.simulator;
/**
* This class provides a list of fake NFC Ndef format Tags.
*/
public class MockNdefMessages {
/**
* A Smart Poster containing a URL and no text.
*/
public static final byte[] SMART_POSTER_URL_NO_TEXT =
new byte[] {(byte) 0xd1, (byte) 0x02, (byte) 0x0f, (byte) 0x53, (byte) 0x70, (byte) 0xd1,
(byte) 0x01, (byte) 0x0b, (byte) 0x55, (byte) 0x01, (byte) 0x67, (byte) 0x6f,
(byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2e, (byte) 0x63,
(byte) 0x6f, (byte) 0x6d};
/**
* A plain text tag in english.
*/
public static final byte[] ENGLISH_PLAIN_TEXT =
new byte[] {(byte) 0xd1, (byte) 0x01, (byte) 0x1c, (byte) 0x54, (byte) 0x02, (byte) 0x65,
(byte) 0x6e, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x20,
(byte) 0x72, (byte) 0x61, (byte) 0x6e, (byte) 0x64, (byte) 0x6f, (byte) 0x6d,
(byte) 0x20, (byte) 0x65, (byte) 0x6e, (byte) 0x67, (byte) 0x6c, (byte) 0x69,
(byte) 0x73, (byte) 0x68, (byte) 0x20, (byte) 0x74, (byte) 0x65, (byte) 0x78,
(byte) 0x74, (byte) 0x2e};
/**
* Smart Poster containing a URL and Text.
*/
public static final byte[] SMART_POSTER_URL_AND_TEXT =
new byte[] {(byte) 0xd1, (byte) 0x02, (byte) 0x1c, (byte) 0x53, (byte) 0x70, (byte) 0x91,
(byte) 0x01, (byte) 0x09, (byte) 0x54, (byte) 0x02, (byte) 0x65, (byte) 0x6e,
(byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65,
(byte) 0x51, (byte) 0x01, (byte) 0x0b, (byte) 0x55, (byte) 0x01, (byte) 0x67,
(byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
(byte) 0x63, (byte) 0x6f, (byte) 0x6d};
/**
* All the mock Ndef tags.
*/
public static final byte[][] ALL_MOCK_MESSAGES =
new byte[][] {SMART_POSTER_URL_NO_TEXT, ENGLISH_PLAIN_TEXT, SMART_POSTER_URL_AND_TEXT};
}