diff --git a/apps/Tag/src/com/android/apps/tag/NdefUtil.java b/apps/Tag/src/com/android/apps/tag/NdefUtil.java index 3035c849b..226597724 100644 --- a/apps/Tag/src/com/android/apps/tag/NdefUtil.java +++ b/apps/Tag/src/com/android/apps/tag/NdefUtil.java @@ -16,17 +16,22 @@ package com.android.apps.tag; +import android.util.Log; import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.Iterables; import com.google.common.primitives.Bytes; +import com.trustedlogic.trustednfc.android.NdefMessage; import com.trustedlogic.trustednfc.android.NdefRecord; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Utilities for dealing with conversions to and from NdefRecords. @@ -133,7 +138,7 @@ public class NdefUtil { String prefix = URI_PREFIX_MAP.get(payload[0]); byte[] fullUri = Bytes.concat( prefix.getBytes(Charsets.UTF_8), - Arrays.copyOfRange(payload, 1, payload.length - 1)); + Arrays.copyOfRange(payload, 1, payload.length)); return new URI(new String(fullUri, Charsets.UTF_8)); } @@ -185,7 +190,50 @@ public class NdefUtil { textEncoding); } catch (UnsupportedEncodingException e) { // should never happen unless we get a malformed tag. - throw new RuntimeException(e); + throw new IllegalArgumentException(e); + } + } + + public static boolean isText(NdefRecord record) { + try { + toText(record); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + public static Iterable getTextFields(NdefMessage message) { + return Iterables.filter(getObjects(message), String.class); + } + + public static Iterable getURIs(NdefMessage message) { + return Iterables.filter(getObjects(message), URI.class); + } + + /** + * Parse the provided {@code NdefMessage}, extracting all known + * objects from the message. Typically this list will consist of + * {@link String}s corresponding to NDEF text records, or {@link URI}s + * corresponding to NDEF URI records. + *

+ * TODO: Is this API too generic? Should we keep it? + */ + private static Iterable getObjects(NdefMessage message) { + try { + List retval = new ArrayList(); + for (NdefRecord record : message.getRecords()) { + if (isURI(record)) { + retval.add(toURI(record)); + } else if (isText(record)) { + retval.add(toText(record)); + } else if (SmartPoster.isPoster(record)) { + retval.add(SmartPoster.from(record)); + } + } + return retval; + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); } } } diff --git a/apps/Tag/src/com/android/apps/tag/SmartPoster.java b/apps/Tag/src/com/android/apps/tag/SmartPoster.java new file mode 100644 index 000000000..509da92aa --- /dev/null +++ b/apps/Tag/src/com/android/apps/tag/SmartPoster.java @@ -0,0 +1,95 @@ +/* + * 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.android.apps.tag; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.trustedlogic.trustednfc.android.NdefMessage; +import com.trustedlogic.trustednfc.android.NdefRecord; +import com.trustedlogic.trustednfc.android.NfcException; + +import javax.annotation.Nullable; +import java.net.URI; +import java.util.Arrays; + +/** + * A representation of an NFC Forum "Smart Poster". + */ +public class SmartPoster { + + /** + * 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 String titleRecord; + + /** + * 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 URI uriRecord; + + private SmartPoster(URI uri, @Nullable String title) { + uriRecord = Preconditions.checkNotNull(uri); + titleRecord = title; + } + + public URI getURI() { + return uriRecord; + } + + /** + * Returns the title of the smartposter. This may be {@code null}. + */ + public String getTitle() { + return titleRecord; + } + + public static SmartPoster from(NdefRecord record) { + Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN_TYPE); + Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.TYPE_SMART_POSTER)); + try { + NdefMessage subRecords = new NdefMessage(record.getPayload()); + URI uri = Iterables.getOnlyElement(NdefUtil.getURIs(subRecords)); + Iterable textFields = NdefUtil.getTextFields(subRecords); + String title = null; + if (!Iterables.isEmpty(textFields)) { + title = Iterables.get(textFields, 0); + } + + return new SmartPoster(uri, title); + } catch (NfcException e) { + throw new IllegalArgumentException(e); + } + } + + public static boolean isPoster(NdefRecord record) { + try { + from(record); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } +} diff --git a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java index 1139ebcf7..12a78b14d 100644 --- a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java +++ b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java @@ -52,7 +52,7 @@ public class TagDBHelper extends SQLiteOpenHelper { * consists of the text "NFC Forum Type 4 Tag" in english combined with * the URL "http://www.nxp.com/nfc" */ - private static final byte[] REAL_NFC_MSG = new byte[] { + public static final byte[] REAL_NFC_MSG = new byte[] { (byte) 0xd1, // MB=1 ME=1 CF=0 SR=1 IL=0 TNF=001 (byte) 0x02, // Type Length = 2 (byte) 0x2b, // Payload Length = 43 diff --git a/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java b/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java new file mode 100644 index 000000000..73039bd38 --- /dev/null +++ b/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.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.android.apps.tag; + +import android.test.AndroidTestCase; +import com.trustedlogic.trustednfc.android.NdefMessage; + +/** + * Tests for {@link SmartPoster}. + */ +public class SmartPosterTest extends AndroidTestCase { + public void testSmartPoster() throws Exception { + NdefMessage msg = new NdefMessage(TagDBHelper.REAL_NFC_MSG); + + SmartPoster poster = SmartPoster.from(msg.getRecords()[0]); + assertEquals("NFC Forum Type 4 Tag", poster.getTitle()); + assertEquals("http://www.nxp.com/nfc", poster.getURI().toString()); + } +}