TextRecord.java revision f34e4d5ec99fd7883bbfcdc0f644d9d6e4df6c97
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.apps.tag.record;
18
19import com.android.apps.tag.R;
20import com.google.common.annotations.VisibleForTesting;
21import com.google.common.base.Preconditions;
22import com.google.common.primitives.Bytes;
23
24import android.app.Activity;
25import android.nfc.NdefRecord;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.widget.TextView;
30
31import java.io.UnsupportedEncodingException;
32import java.nio.charset.Charset;
33import java.util.Arrays;
34import java.util.Locale;
35
36/**
37 * An NFC Text Record
38 */
39public class TextRecord implements ParsedNdefRecord {
40
41    /** ISO/IANA language code */
42    private final String mLanguageCode;
43    private final String mText;
44
45    private TextRecord(String languageCode, String text) {
46        mLanguageCode = Preconditions.checkNotNull(languageCode);
47        mText = Preconditions.checkNotNull(text);
48    }
49
50    @Override
51    public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent) {
52        TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
53        text.setText(mText);
54        return text;
55    }
56
57    public String getText() {
58        return mText;
59    }
60
61    /**
62     * Returns the ISO/IANA language code associated with this text element.
63     *
64     * TODO: this should return a {@link java.util.Locale}
65     */
66    @VisibleForTesting
67    public String getLanguageCode() {
68        return mLanguageCode;
69    }
70
71    // TODO: deal with text fields which span multiple NdefRecords
72    public static TextRecord parse(NdefRecord record) {
73        Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
74        Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT));
75        try {
76
77            byte[] payload = record.getPayload();
78
79            /*
80             * payload[0] contains the "Status Byte Encodings" field, per
81             * the NFC Forum "Text Record Type Definition" section 3.2.1.
82             *
83             * bit7 is the Text Encoding Field.
84             *
85             * if (Bit_7 == 0): The text is encoded in UTF-8
86             * if (Bit_7 == 1): The text is encoded in UTF16
87             *
88             * Bit_6 is reserved for future use and must be set to zero.
89             *
90             * Bits 5 to 0 are the length of the IANA language code.
91             */
92
93            String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
94            int languageCodeLength = payload[0] & 0077;
95
96            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
97            String text = new String(payload,
98                    languageCodeLength + 1,
99                    payload.length - languageCodeLength - 1,
100                    textEncoding);
101            return new TextRecord(languageCode, text);
102
103        } catch (UnsupportedEncodingException e) {
104            // should never happen unless we get a malformed tag.
105            throw new IllegalArgumentException(e);
106        }
107    }
108
109    public static boolean isText(NdefRecord record) {
110        try {
111            parse(record);
112            return true;
113        } catch (IllegalArgumentException e) {
114            return false;
115        }
116    }
117
118    @VisibleForTesting
119    public static NdefRecord newTextRecord(String text, Locale locale) {
120        return newTextRecord(text, locale, true);
121    }
122
123    public static NdefRecord newTextRecord(String text, Locale locale, boolean encodeInUtf8) {
124        Preconditions.checkNotNull(text);
125        Preconditions.checkNotNull(locale);
126
127        byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
128
129        Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
130        byte[] textBytes = text.getBytes(utfEncoding);
131
132        int utfBit = encodeInUtf8 ? 0 : (1 << 7);
133        char status = (char) (utfBit + langBytes.length);
134
135        byte[] data = Bytes.concat(
136           new byte[] { (byte) status },
137           langBytes,
138           textBytes
139        );
140
141        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
142    }
143}
144