17949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton/* 27949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Copyright (C) 2010 The Android Open Source Project 37949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 47949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License"); 57949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * you may not use this file except in compliance with the License. 67949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * You may obtain a copy of the License at 77949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 87949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * http://www.apache.org/licenses/LICENSE-2.0 97949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 107949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Unless required by applicable law or agreed to in writing, software 117949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS, 127949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * See the License for the specific language governing permissions and 147949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * limitations under the License. 157949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton */ 167949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 177949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonpackage com.android.apps.tag.record; 187949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 197949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport com.android.apps.tag.R; 202ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevichimport com.google.common.annotations.VisibleForTesting; 217949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport com.google.common.base.Preconditions; 222ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevichimport com.google.common.primitives.Bytes; 237949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 247949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.app.Activity; 25a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parksimport android.content.Context; 267949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.nfc.NdefRecord; 277949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.view.LayoutInflater; 287949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.view.View; 297949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.view.ViewGroup; 307949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport android.widget.TextView; 317949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 327949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport java.io.UnsupportedEncodingException; 332ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevichimport java.nio.charset.Charset; 347949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamiltonimport java.util.Arrays; 352ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevichimport java.util.Locale; 367949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 377949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton/** 387949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * An NFC Text Record 397949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton */ 4040d9a0cd0a37d184ee737b2d7138a39e4292ce3eJason parkspublic class TextRecord extends ParsedNdefRecord { 417949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 4258fbc09c19d87b6463bb68f74eefc43af1c13dbfJeff Hamilton public static final String RECORD_TYPE = "TextRecord"; 4358fbc09c19d87b6463bb68f74eefc43af1c13dbfJeff Hamilton 447949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton /** ISO/IANA language code */ 457949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton private final String mLanguageCode; 467949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton private final String mText; 477949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 487949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton private TextRecord(String languageCode, String text) { 497949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton mLanguageCode = Preconditions.checkNotNull(languageCode); 507949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton mText = Preconditions.checkNotNull(text); 517949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 527949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 537949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton @Override 5440d9a0cd0a37d184ee737b2d7138a39e4292ce3eJason parks public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) { 557949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false); 567949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton text.setText(mText); 577949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return text; 587949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 597949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 60a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parks @Override 61a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parks public String getSnippet(Context context, Locale locale) { 62a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parks return mText; 63a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parks } 64a6855221fb6e5c6c19d31283a18ced44cccdf442Jason parks 657949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton public String getText() { 667949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return mText; 677949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 687949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 697949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton /** 707949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Returns the ISO/IANA language code associated with this text element. 717949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 727949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * TODO: this should return a {@link java.util.Locale} 737949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton */ 742ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich @VisibleForTesting 757949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton public String getLanguageCode() { 767949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return mLanguageCode; 777949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 787949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 797949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton // TODO: deal with text fields which span multiple NdefRecords 807949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton public static TextRecord parse(NdefRecord record) { 817949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN); 827949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)); 837949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton try { 847949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 857949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton byte[] payload = record.getPayload(); 868722731539a5dad00bb5db54df17b6db11f751e7Nick Kralevich Preconditions.checkArgument(payload.length > 0); 877949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 887949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton /* 897949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * payload[0] contains the "Status Byte Encodings" field, per 907949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * the NFC Forum "Text Record Type Definition" section 3.2.1. 917949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 927949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * bit7 is the Text Encoding Field. 937949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 947949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * if (Bit_7 == 0): The text is encoded in UTF-8 957949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * if (Bit_7 == 1): The text is encoded in UTF16 967949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 977949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Bit_6 is reserved for future use and must be set to zero. 987949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * 997949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton * Bits 5 to 0 are the length of the IANA language code. 1007949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton */ 1017949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 1027949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16"; 1037949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton int languageCodeLength = payload[0] & 0077; 10413aac27873c59071b321b38475d57a849e328e6aNick Kralevich Preconditions.checkArgument(payload.length - languageCodeLength - 1 >= 0); 1057949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 1067949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII"); 1077949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton String text = new String(payload, 1087949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton languageCodeLength + 1, 1097949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton payload.length - languageCodeLength - 1, 1107949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton textEncoding); 1117949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return new TextRecord(languageCode, text); 1127949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 1137949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } catch (UnsupportedEncodingException e) { 1147949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton // should never happen unless we get a malformed tag. 1157949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton throw new IllegalArgumentException(e); 1167949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 1177949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 1187949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton 1197949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton public static boolean isText(NdefRecord record) { 1207949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton try { 1217949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton parse(record); 1227949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return true; 1237949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } catch (IllegalArgumentException e) { 1247949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton return false; 1257949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 1267949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton } 1272ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 1282ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich @VisibleForTesting 1292ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich public static NdefRecord newTextRecord(String text, Locale locale) { 1302ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich return newTextRecord(text, locale, true); 1312ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich } 1322ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 1332ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich public static NdefRecord newTextRecord(String text, Locale locale, boolean encodeInUtf8) { 1342ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich Preconditions.checkNotNull(text); 1352ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich Preconditions.checkNotNull(locale); 1362ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 137f34e4d5ec99fd7883bbfcdc0f644d9d6e4df6c97Jeff Hamilton byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); 1382ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 139f34e4d5ec99fd7883bbfcdc0f644d9d6e4df6c97Jeff Hamilton Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); 1402ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich byte[] textBytes = text.getBytes(utfEncoding); 1412ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 1422ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich int utfBit = encodeInUtf8 ? 0 : (1 << 7); 1432ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich char status = (char) (utfBit + langBytes.length); 1442ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 1452ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich byte[] data = Bytes.concat( 1462ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich new byte[] { (byte) status }, 1472ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich langBytes, 1482ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich textBytes 1492ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich ); 1502ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich 1512ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); 1522ef3e3a274141d0fec3e108b2ee8ba02be78c1c2Nick Kralevich } 1537949534630e1b14cf73a6ad3c0606ec5be2c480cJeff Hamilton} 154