From cbb9f0d00e119c35e1d1fbd03fbfea2885cfe1e9 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 29 Oct 2010 15:22:12 -0700 Subject: [PATCH 1/2] Remove .classpath and .project files During review of the patch set for native audio sample, someone mentioned there should not be these files in git. But we seem to be inconsistent about this (for example these files). Can you please let me know whether they should be there or not, and then I will update the native audio patch set. Thanks! Change-Id: I6d27f4936f8c60efb05a89a4ba11e77e66aaf539 --- samples/TicTacToeLib/.classpath | 7 ------ samples/TicTacToeLib/.project | 33 -------------------------- samples/TicTacToeMain/.classpath | 8 ------- samples/TicTacToeMain/.project | 40 -------------------------------- 4 files changed, 88 deletions(-) delete mode 100755 samples/TicTacToeLib/.classpath delete mode 100755 samples/TicTacToeLib/.project delete mode 100755 samples/TicTacToeMain/.classpath delete mode 100755 samples/TicTacToeMain/.project diff --git a/samples/TicTacToeLib/.classpath b/samples/TicTacToeLib/.classpath deleted file mode 100755 index 6e9239ff0..000000000 --- a/samples/TicTacToeLib/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/samples/TicTacToeLib/.project b/samples/TicTacToeLib/.project deleted file mode 100755 index c0589f6ea..000000000 --- a/samples/TicTacToeLib/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - TicTacToeLib - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/samples/TicTacToeMain/.classpath b/samples/TicTacToeMain/.classpath deleted file mode 100755 index 2db729987..000000000 --- a/samples/TicTacToeMain/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/samples/TicTacToeMain/.project b/samples/TicTacToeMain/.project deleted file mode 100755 index 9d7485aa5..000000000 --- a/samples/TicTacToeMain/.project +++ /dev/null @@ -1,40 +0,0 @@ - - - TicTacToeMain - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - - - TicTacToeLib - 2 - _android_TicTacToeLib/src - - - From 150dd1693ff06a7683076eebe669277631dedf06 Mon Sep 17 00:00:00 2001 From: Megha Joshi Date: Tue, 2 Nov 2010 23:27:24 -0700 Subject: [PATCH 2/2] NFC sample app (DO NOT MERGE) Change-Id: I1d1d67bbc6760cea1a0c6eccfa4d9f7b1398cd4d --- samples/NFCDemo/Android.mk | 18 ++ samples/NFCDemo/AndroidManifest.xml | 49 ++++ samples/NFCDemo/_index.html | 129 ++++++++++ samples/NFCDemo/res/drawable/icon.png | Bin 0 -> 2574 bytes samples/NFCDemo/res/layout/tag_divider.xml | 22 ++ samples/NFCDemo/res/layout/tag_text.xml | 28 +++ samples/NFCDemo/res/layout/tag_viewer.xml | 32 +++ .../res/raw/discovered_tag_notification.ogg | Bin 0 -> 7871 bytes samples/NFCDemo/res/values/strings.xml | 28 +++ .../android/nfc/NdefMessageParser.java | 57 +++++ .../com/example/android/nfc/TagViewer.java | 124 ++++++++++ .../android/nfc/record/ParsedNdefRecord.java | 33 +++ .../android/nfc/record/SmartPoster.java | 222 ++++++++++++++++++ .../android/nfc/record/TextRecord.java | 104 ++++++++ .../example/android/nfc/record/UriRecord.java | 159 +++++++++++++ .../nfc/simulator/FakeTagsActivity.java | 108 +++++++++ .../nfc/simulator/MockNdefMessages.java | 59 +++++ 17 files changed, 1172 insertions(+) create mode 100644 samples/NFCDemo/Android.mk create mode 100644 samples/NFCDemo/AndroidManifest.xml create mode 100644 samples/NFCDemo/_index.html create mode 100644 samples/NFCDemo/res/drawable/icon.png create mode 100644 samples/NFCDemo/res/layout/tag_divider.xml create mode 100644 samples/NFCDemo/res/layout/tag_text.xml create mode 100644 samples/NFCDemo/res/layout/tag_viewer.xml create mode 100755 samples/NFCDemo/res/raw/discovered_tag_notification.ogg create mode 100644 samples/NFCDemo/res/values/strings.xml create mode 100644 samples/NFCDemo/src/com/example/android/nfc/NdefMessageParser.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/TagViewer.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/record/ParsedNdefRecord.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/record/SmartPoster.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/record/TextRecord.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/record/UriRecord.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/simulator/FakeTagsActivity.java create mode 100644 samples/NFCDemo/src/com/example/android/nfc/simulator/MockNdefMessages.java diff --git a/samples/NFCDemo/Android.mk b/samples/NFCDemo/Android.mk new file mode 100644 index 000000000..60815a207 --- /dev/null +++ b/samples/NFCDemo/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := samples tests + +LOCAL_STATIC_JAVA_LIBRARIES := guava + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := NFCDemo + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/samples/NFCDemo/AndroidManifest.xml b/samples/NFCDemo/AndroidManifest.xml new file mode 100644 index 000000000..044690b75 --- /dev/null +++ b/samples/NFCDemo/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/NFCDemo/_index.html b/samples/NFCDemo/_index.html new file mode 100644 index 000000000..d1c53f17d --- /dev/null +++ b/samples/NFCDemo/_index.html @@ -0,0 +1,129 @@ +

+ Near-field Communication or NFC is a standard defined by the + NFC Forum + . + NFC Data Exchange Format (NDEF) defines a common data format between NFC-compliant devices and tags. + This demo application shows how to read a NDEF Tags using using Android 2.3 SDK APIs. + The NFC Tags consist of data encoded in NDEF Message format specified by NFC Forum Type 2 Specification. + Each NDEF message consists of one or more NDEF Records. + + You need a NFC compliant device and a NFC compliant Tag to use this sample app. Or else, you could use + the FakeTagsActivity displayed at launch of this sample app, to generate fake Tag broadcasts from the emulator. +

+ +

The application includes: +

+ +

If you are developing an application that uses the NFC API, remember that the feature + is supported only on Android 2.3 (API level 9) and higher versions of the platform. Also, + among devices running Android 2.3 (API level 9) or higher, not all devices will offer NFC + support. To ensure that your application can only be installed on devices that are capable + of supporting NFC, remember to add the following to the application's manifest before + publishing to Android Market: +

+ +

To control how Android Market filters your application + from devices that do not support NFC, remember to add the following to the application's manifest +

+

For more information about using the NFC API, see the + + android.nfc + + documentation. +

+ diff --git a/samples/NFCDemo/res/drawable/icon.png b/samples/NFCDemo/res/drawable/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a07c69fa5a0f4da5d5efe96eea12a543154dbab6 GIT binary patch literal 2574 zcmV+p3i0)cP)Q`Og{P|8RRXpj5bgrSmEzSMfBn+{{vpNxw?;5UX;iv9sYxy_`IQHs$i<61a_iv^L>h8s-`D(`e@|IgS*Fj zNGM876Gf;3D8*1UX9a%v>yJKD*QkCwW2AirU(L{qNA)JghmGItc;(H<$!ABY&gBy1vJIEUj-b8%el*o|VkG)LqNx#TG>Jvj^jIte!!+RY z)T4j$7+PoF1AkRBf}R#^T=-q|PaK1$c<4UH)Hpq3$4WA|xtr!ZQLC=*vNE>O6E9kp+5X0eKB$6>C(lPwI@3#oY zhS_%x7e|j!$yG?ECXmh~EH~^OeuK}+sWoJse3Z3?ha3n`MM9KvA?uqpEnBg4Q46)7 zM$p%a$@l;+O}vfvx%XjH`}a{(-HHth9!JaUwV0*VqGR48^gWNYN<&~7x)y$e!X>e` zZ5!6KZoxbKuV9XUDI%#M1~IVh?pNSdeb~6@$y`v|yk=XK+fHxnDqnUK4&=QRNyIVf zYbDM*cI>~qIy*a7=z7uqkw@agd(<=y-Q7L!ty_23SGdXmahO<;N=wB+j;lNm%=OHC zy zU|>La6h%92y4IPufI$9>Xu!@y`TaNgtg&41@PwMwBdmSm7)xAWDLoqjZ==P2#*k7! z3o1)cVSI3KP_!?d8G^Lg0FtLXC~JYdxi|c%h~lXEixY=%VSFF@!*3&&9>(Rb|iK54Cx5;s~PY5iaV1het%w`dgQFBAJ;aFK zImQC}(|QaCFYUm1JVfzSc)ebv=)ObI)0jwJb``}Zj9J0n0Xgn*Zc(rFM9$xh_makZbm-at_v5^SW zM1y1SW@%+FuIy*WR)i3A2N_q;(YO`O!A|Ts^%z}9ZepCj3ytlw#x%N_fNrKKtPh`< z|1{UqF`4LxHaCQ79+E=uUXCOZ35jAMRz%R%0(P!0FMv=sk>Nr8%+OzY^c-M9@+fz=G`qa@v4sF5u-2289-#$**LWnyNNDwDf1( zkUiMnw|y$tn>pQP=Vn!#|17L^5AGrjtBkN$D@v)Z7LXc5EFhLB4<;7Wehh)CMqX|W zqsiZaO^benJ_hwa&V0ub$-_HUk**?g6fm9|!@kguU6*zhK)$qn-<3*kFrYPIaqR=V zUaUvk>@F_89b@tHs8R!*QKY;INJ<2_U+K6Ca3e9Gsl2{qY0%a7J?uICWgHuLfj+MB z=GkAN1&ifT#2u}B+2S#~$5jA(Qn^;H%CCmIae4AE-Dsng|Hl*Ov!z72k3ZnJs{pp| z+pW`DDueC#mEWOf=ucJ!dTL}hzOeiS-i?m2E;`EKz4<&Lu~NnW?peqVU^@<+T3KKu z{yrI%Qy-Z%HEvLUz}n^~m?7x`xuCtNR#L2En!T>dQtIKdS#V-Hzt3RtwTeYtmQ&dR z6qXZvac*oc@BUYEH%@Ylv_1&tSjkbzzU6*h1(3^C`;1z;g_SmOtclS?KWk2VYE zM*oS<=C483XckW?GN|1jfh3Ro(h + + + \ No newline at end of file diff --git a/samples/NFCDemo/res/layout/tag_text.xml b/samples/NFCDemo/res/layout/tag_text.xml new file mode 100644 index 000000000..59cbbfbc9 --- /dev/null +++ b/samples/NFCDemo/res/layout/tag_text.xml @@ -0,0 +1,28 @@ + + + + \ No newline at end of file diff --git a/samples/NFCDemo/res/layout/tag_viewer.xml b/samples/NFCDemo/res/layout/tag_viewer.xml new file mode 100644 index 000000000..a429ef95d --- /dev/null +++ b/samples/NFCDemo/res/layout/tag_viewer.xml @@ -0,0 +1,32 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/NFCDemo/res/raw/discovered_tag_notification.ogg b/samples/NFCDemo/res/raw/discovered_tag_notification.ogg new file mode 100755 index 0000000000000000000000000000000000000000..7bf5df7efd94e60aa8dee94eedcfc1321d8470bf GIT binary patch literal 7871 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 literal 0 HcmV?d00001 diff --git a/samples/NFCDemo/res/values/strings.xml b/samples/NFCDemo/res/values/strings.xml new file mode 100644 index 000000000..251d2f66c --- /dev/null +++ b/samples/NFCDemo/res/values/strings.xml @@ -0,0 +1,28 @@ + + + + + + + NFCDemo + + + New tag collected + + + Text + + diff --git a/samples/NFCDemo/src/com/example/android/nfc/NdefMessageParser.java b/samples/NFCDemo/src/com/example/android/nfc/NdefMessageParser.java new file mode 100644 index 000000000..7372810b3 --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/NdefMessageParser.java @@ -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 parse(NdefMessage message) { + return getRecords(message.getRecords()); + } + + public static List getRecords(NdefRecord[] records) { + List elements = new ArrayList(); + 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; + } +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/TagViewer.java b/samples/NFCDemo/src/com/example/android/nfc/TagViewer.java new file mode 100644 index 000000000..01dc0bd80 --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/TagViewer.java @@ -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 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); + } +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/record/ParsedNdefRecord.java b/samples/NFCDemo/src/com/example/android/nfc/record/ParsedNdefRecord.java new file mode 100644 index 000000000..b706ae8cb --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/record/ParsedNdefRecord.java @@ -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); + +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/record/SmartPoster.java b/samples/NFCDemo/src/com/example/android/nfc/record/SmartPoster.java new file mode 100644 index 000000000..f86d3ef63 --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/record/SmartPoster.java @@ -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 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 getFirstIfExists(Iterable elements, Class type) { + Iterable 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 LOOKUP; + static { + ImmutableMap.Builder 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); + } +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/record/TextRecord.java b/samples/NFCDemo/src/com/example/android/nfc/record/TextRecord.java new file mode 100644 index 000000000..8041cca9b --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/record/TextRecord.java @@ -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; + } + } +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/record/UriRecord.java b/samples/NFCDemo/src/com/example/android/nfc/record/UriRecord.java new file mode 100644 index 000000000..452ebb282 --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/record/UriRecord.java @@ -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 URI_PREFIX_MAP = ImmutableBiMap.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]; +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/simulator/FakeTagsActivity.java b/samples/NFCDemo/src/com/example/android/nfc/simulator/FakeTagsActivity.java new file mode 100644 index 000000000..313bab499 --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/simulator/FakeTagsActivity.java @@ -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 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 adapter = new ArrayAdapter( + 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); + } +} diff --git a/samples/NFCDemo/src/com/example/android/nfc/simulator/MockNdefMessages.java b/samples/NFCDemo/src/com/example/android/nfc/simulator/MockNdefMessages.java new file mode 100644 index 000000000..52a122f7e --- /dev/null +++ b/samples/NFCDemo/src/com/example/android/nfc/simulator/MockNdefMessages.java @@ -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}; +}