6ea4e807bd75b0ea587ad7074d91d15de2f5f54d
[android/platform/packages/apps/Tag.git] / src / com / android / apps / tag / record / TextRecord.java
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
17 package com.android.apps.tag.record;
18
19 import com.android.apps.tag.R;
20 import com.android.apps.tag.record.UriRecord.UriRecordEditInfo;
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.base.Preconditions;
23 import com.google.common.primitives.Bytes;
24
25 import android.app.Activity;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.net.Uri;
31 import android.nfc.NdefRecord;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 import android.text.TextUtils;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.ImageView;
39 import android.widget.TextView;
40
41 import java.io.UnsupportedEncodingException;
42 import java.nio.charset.Charset;
43 import java.util.Arrays;
44 import java.util.List;
45 import java.util.Locale;
46
47 /**
48  * An NFC Text Record
49  */
50 public class TextRecord extends ParsedNdefRecord {
51
52     public static final String RECORD_TYPE = "TextRecord";
53
54     /** ISO/IANA language code */
55     private final String mLanguageCode;
56     private final String mText;
57
58     private TextRecord(String languageCode, String text) {
59         mLanguageCode = Preconditions.checkNotNull(languageCode);
60         mText = Preconditions.checkNotNull(text);
61     }
62
63     @Override
64     public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent, int offset) {
65         TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
66         text.setText(mText);
67         return text;
68     }
69
70     @Override
71     public String getSnippet(Context context, Locale locale) {
72         return mText;
73     }
74
75     public String getText() {
76         return mText;
77     }
78
79     /**
80      * Returns the ISO/IANA language code associated with this text element.
81      *
82      * TODO: this should return a {@link java.util.Locale}
83      */
84     @VisibleForTesting
85     public String getLanguageCode() {
86         return mLanguageCode;
87     }
88
89     @Override
90     public RecordEditInfo getEditInfo(Activity host) {
91         return new TextRecordEditInfo(mText);
92     }
93
94     /**
95      * Returns a view in a list of record types for adding new records to a message.
96      */
97     public static View getAddView(Context context, LayoutInflater inflater, ViewGroup parent) {
98         ViewGroup root = (ViewGroup) inflater.inflate(
99                 R.layout.tag_add_record_list_item, parent, false);
100         ((ImageView) root.findViewById(R.id.image)).setImageResource(R.drawable.ic_launcher_nfc);
101         ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.tag_text));
102
103         root.setTag(new TextRecordEditInfo(""));
104         return root;
105     }
106
107     // TODO: deal with text fields which span multiple NdefRecords
108     public static TextRecord parse(NdefRecord record) {
109         Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
110         Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT));
111         try {
112
113             byte[] payload = record.getPayload();
114
115             /*
116              * payload[0] contains the "Status Byte Encodings" field, per
117              * the NFC Forum "Text Record Type Definition" section 3.2.1.
118              *
119              * bit7 is the Text Encoding Field.
120              *
121              * if (Bit_7 == 0): The text is encoded in UTF-8
122              * if (Bit_7 == 1): The text is encoded in UTF16
123              *
124              * Bit_6 is reserved for future use and must be set to zero.
125              *
126              * Bits 5 to 0 are the length of the IANA language code.
127              */
128
129             String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
130             int languageCodeLength = payload[0] & 0077;
131             Preconditions.checkArgument(payload.length - languageCodeLength - 1 >= 0);
132
133             String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
134             String text = new String(payload,
135                     languageCodeLength + 1,
136                     payload.length - languageCodeLength - 1,
137                     textEncoding);
138             return new TextRecord(languageCode, text);
139
140         } catch (UnsupportedEncodingException e) {
141             // should never happen unless we get a malformed tag.
142             throw new IllegalArgumentException(e);
143         }
144     }
145
146     public static boolean isText(NdefRecord record) {
147         try {
148             parse(record);
149             return true;
150         } catch (IllegalArgumentException e) {
151             return false;
152         }
153     }
154
155     @VisibleForTesting
156     public static NdefRecord newTextRecord(String text, Locale locale) {
157         return newTextRecord(text, locale, true);
158     }
159
160     public static NdefRecord newTextRecord(String text, Locale locale, boolean encodeInUtf8) {
161         Preconditions.checkNotNull(text);
162         Preconditions.checkNotNull(locale);
163
164         byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
165
166         Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
167         byte[] textBytes = text.getBytes(utfEncoding);
168
169         int utfBit = encodeInUtf8 ? 0 : (1 << 7);
170         char status = (char) (utfBit + langBytes.length);
171
172         byte[] data = Bytes.concat(
173            new byte[] { (byte) status },
174            langBytes,
175            textBytes
176         );
177
178         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
179     }
180
181     public static class TextRecordEditInfo extends AbstractTextRecordEditInfo {
182         public TextRecordEditInfo(String text) {
183             super(text);
184         }
185
186         public TextRecordEditInfo(Parcel in) {
187             super(in);
188         }
189
190         @Override
191         public int getLayoutId() {
192             return R.layout.tag_edit_text;
193         }
194
195         @Override
196         public NdefRecord getValue() {
197             String text = getCurrentText();
198             if (TextUtils.isEmpty(text)) text = "";
199             return TextRecord.newTextRecord(text, Locale.getDefault(), true);
200         }
201
202         public static final Parcelable.Creator<TextRecordEditInfo> CREATOR =
203                 new Parcelable.Creator<TextRecordEditInfo>() {
204             @Override
205             public TextRecordEditInfo createFromParcel(Parcel in) {
206                 return new TextRecordEditInfo(in);
207             }
208
209             @Override
210             public TextRecordEditInfo[] newArray(int size) {
211                 return new TextRecordEditInfo[size];
212             }
213         };
214     }
215 }