/* * Copyright (C) 2011 Stanford University MobiSocial Lab * http://mobisocial.stanford.edu * * 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 mobisocial.nfc; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; import java.util.Arrays; import android.net.Uri; import android.nfc.FormatException; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.util.Base64; /** * A utility class for generating NDEF messages. * @see NdefMessage */ public class NdefFactory { /** * An RTD indicating an Android Application Record. */ public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes(); private static final String[] URI_PREFIXES = new String[] { "", "http://www.", "https://www.", "http://", "https://", "tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.", "ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://", "news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:", "sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://", "tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:", }; public static NdefMessage fromUri(Uri uri) { return fromUri(uri.toString()); } public static NdefMessage fromUri(URI uri) { return fromUri(uri.toString()); } public static NdefMessage fromUri(String uri) { try { int prefix = 0; for (int i = 1; i < URI_PREFIXES.length; i++) { if (uri.startsWith(URI_PREFIXES[i])) { prefix = i; break; } } if (prefix > 0) uri = uri.substring(URI_PREFIXES[prefix].length()); int len = uri.length(); byte[] payload = new byte[len + 1]; payload[0] = (byte) prefix; System.arraycopy(uri.getBytes("UTF-8"), 0, payload, 1, len); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload); NdefRecord[] records = new NdefRecord[] { record }; return new NdefMessage(records); } catch (NoClassDefFoundError e) { return null; } catch (UnsupportedEncodingException e) { // no UTF-8? really? return null; } } public static NdefMessage fromUrl(URL url) { return fromUri(url.toString()); } public static NdefMessage fromMime(String mimeType, byte[] data) { try { NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeType.getBytes(), new byte[0], data); NdefRecord[] records = new NdefRecord[] { record }; return new NdefMessage(records); } catch (NoClassDefFoundError e) { return null; } } /** * Creates an NDEF message with a single text record, with language * code "en" and the given text, encoded using UTF-8. */ public static NdefMessage fromText(String text) { try { byte[] textBytes = text.getBytes(); byte[] textPayload = new byte[textBytes.length + 3]; textPayload[0] = 0x02; // Status byte; UTF-8 and "en" encoding. textPayload[1] = 'e'; textPayload[2] = 'n'; System.arraycopy(textBytes, 0, textPayload, 3, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], textPayload); NdefRecord[] records = new NdefRecord[] { record }; return new NdefMessage(records); } catch (NoClassDefFoundError e) { return null; } } /** * Creates an NDEF message with a single text record, with the given * text content (UTF-8 encoded) and language code. */ public static NdefMessage fromText(String text, String languageCode) { try { int languageCodeLength = languageCode.length(); int textLength = text.length(); byte[] textPayload = new byte[textLength + 1 + languageCodeLength]; textPayload[0] = (byte)(0x3F & languageCodeLength); // UTF-8 with the given language code length. System.arraycopy(languageCode.getBytes(), 0, textPayload, 1, languageCodeLength); System.arraycopy(text.getBytes(), 0, textPayload, 1 + languageCodeLength, textLength); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], textPayload); NdefRecord[] records = new NdefRecord[] { record }; return new NdefMessage(records); } catch (NoClassDefFoundError e) { return null; } } public static final NdefMessage getEmptyNdef() { byte[] empty = new byte[] {}; NdefRecord[] records = new NdefRecord[1]; records[0] = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, empty, empty, empty); NdefMessage ndef = new NdefMessage(records); return ndef; } public static final boolean isEmpty(NdefMessage ndef) { return (ndef == null || ndef.equals(getEmptyNdef())); } /** * Converts an Ndef message encoded in uri format to an NdefMessage. * {@hide} */ public static final NdefMessage fromNdefUri(Uri uri) { if (!"ndef".equals(uri.getScheme())) { throw new IllegalArgumentException("Not an ndef:// uri. did you mean NdefFactory.fromUri()?"); } NdefMessage wrappedNdef; try { wrappedNdef = new NdefMessage(android.util.Base64.decode( uri.getPath().substring(1), Base64.URL_SAFE)); } catch (FormatException e) { throw new IllegalArgumentException("Format error."); } return wrappedNdef; } public static final Uri parseUri(NdefRecord record) throws FormatException { int tnf = record.getTnf(); if (tnf == NdefRecord.TNF_ABSOLUTE_URI) { return Uri.parse(new String(record.getType())); } if (tnf == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(NdefRecord.RTD_URI, record.getType())) { byte[] payload = record.getPayload(); int pre = (int)payload[0]; if (!(pre >= 0 && pre < URI_PREFIXES.length)) { throw new FormatException("Unknown uri prefix: " + pre); } String prefix = URI_PREFIXES[pre]; String uriStr = new StringBuilder() .append(prefix).append(new String(payload, 1, payload.length - 1)) .toString(); return Uri.parse(uriStr); } throw new FormatException("NdefRecord is not a uri."); } /** * Converts an Ndef message to its uri encoding, using the * {code ndef://} scheme. */ // TODO Switch for the appropriate type /* public static final Uri toNdefUri(NdefMessage ndef) { return Uri.parse("ndef://url/" + Base64.encodeToString(ndef.toByteArray(), Base64.URL_SAFE)); }*/ /** * <p> * From Android SDK, version 14. (C) 2010 The Android Open Source Project * <p> * Creates an Android application NDEF record. * <p> * This record indicates to other Android devices the package that should be * used to handle the rest of the NDEF message. You can embed this record * anywhere into your NDEF message to ensure that the intended package * receives the message. * <p> * When an Android device dispatches an {@link NdefMessage} containing one * or more Android application records, the applications contained in those * records will be the preferred target for the NDEF_DISCOVERED intent, in * the order in which they appear in the {@link NdefMessage}. This dispatch * behavior was first added to Android in Ice Cream Sandwich. * <p> * If none of the applications are installed on the device, a Market link * will be opened to the first application. * <p> * Note that Android application records do not overrule applications that * have called {@link NfcAdapter#enableForegroundDispatch}. * * @param packageName Android package name * @return Android application NDEF record */ public static NdefRecord createApplicationRecord(String packageName) { try { return new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {}, packageName.getBytes("US-ASCII")); } catch (UnsupportedEncodingException e) { return null; } } }