package com.android.apps.tag;
-import com.android.apps.tag.record.ParsedNdefRecord;
-import com.android.apps.tag.record.SmartPoster;
-import com.android.apps.tag.record.TextRecord;
-import com.android.apps.tag.record.UriRecord;
-import com.google.common.collect.Iterables;
-import com.google.common.primitives.Bytes;
-
import android.net.Uri;
-import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
+import com.google.common.primitives.Bytes;
+
import java.nio.charset.Charsets;
-import java.util.ArrayList;
-import java.util.List;
/**
* Utilities for dealing with conversions to and from NdefRecords.
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_URI, EMPTY, payload);
}
-
- public static Iterable<TextRecord> getTextFields(NdefMessage message) {
- return Iterables.filter(getObjects(message), TextRecord.class);
- }
-
- public static Iterable<UriRecord> getUris(NdefMessage message) {
- return Iterables.filter(getObjects(message), UriRecord.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.
- * <p>
- * TODO: Is this API too generic? Should we keep it?
- */
- public static Iterable<ParsedNdefRecord> getObjects(NdefMessage message) {
- List<ParsedNdefRecord> retval = new ArrayList<ParsedNdefRecord>();
- for (NdefRecord record : message.getRecords()) {
- if (UriRecord.isUri(record)) {
- retval.add(UriRecord.parse(record));
- } else if (TextRecord.isText(record)) {
- retval.add(TextRecord.parse(record));
- } else if (SmartPoster.isPoster(record)) {
- retval.add(SmartPoster.parse(record));
- }
- }
- return retval;
- }
}
package com.android.apps.tag;
-import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
-
import android.content.Context;
import android.database.Cursor;
-import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.Adapter;
import android.widget.CursorAdapter;
import android.widget.TextView;
-import com.android.apps.tag.record.SmartPoster;
-import com.android.apps.tag.record.TextRecord;
-import com.android.apps.tag.record.UriRecord;
+
+import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
+import com.android.apps.tag.message.NdefMessageParser;
+import com.android.apps.tag.message.ParsedNdefMessage;
+
+import java.util.Locale;
/**
* A custom {@link Adapter} that renders tag entries for a list.
if (msg == null) {
mainLine.setText("Invalid tag");
} else {
- try {
- SmartPoster poster = SmartPoster.parse(msg.getRecords()[0]);
- TextRecord title = poster.getTitle();
- if (title != null) {
- mainLine.setText(title.getText());
- }
- } catch (IllegalArgumentException e) {
- // Not a smart poster
- NdefRecord record = msg.getRecords()[0];
- Uri uri = null;
- try {
- uri = UriRecord.parse(record).getUri();
- mainLine.setText(uri.toString());
- } catch (IllegalArgumentException e2) {
- mainLine.setText("Not a smart poster or URL");
- }
- }
+ ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msg);
+ mainLine.setText(parsedMsg.getSnippet(Locale.getDefault()));
}
dateLine.setText(DateUtils.getRelativeTimeSpanString(
context, cursor.getLong(cursor.getColumnIndex(NdefMessagesTable.DATE))));
package com.android.apps.tag;
-import com.google.common.annotations.VisibleForTesting;
-
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
+import com.google.common.annotations.VisibleForTesting;
+
/**
* Database utilities for the saved tags.
*/
package com.android.apps.tag;
-import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.view.View;
import android.widget.ListView;
+import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
+
/**
* An {@link Activity} that displays a flat list of tags that can be "opened".
*/
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.apps.tag.record.ParsedNdefRecord;
-import com.android.apps.tag.record.SmartPoster;
-import com.android.apps.tag.record.TextRecord;
-import com.android.apps.tag.record.UriRecord;
+
+import com.android.apps.tag.message.NdefMessageParser;
+import com.android.apps.tag.message.ParsedNdefMessage;
+
+import java.util.Locale;
/**
* An {@link Activity} which handles a broadcast of a new tag that the device just discovered.
// The body of the dialog should use the light theme
// Build the views from the logical records in the messages
- boolean first = true;
for (NdefMessage msg : msgs) {
- Iterable<ParsedNdefRecord> objects = NdefUtil.getObjects(msg);
- for (ParsedNdefRecord object : objects) {
- if (!first) {
- list.addView(inflater.inflate(R.layout.tag_divider, list, false));
- first = false;
- }
-
- if (object instanceof TextRecord) {
- TextRecord textRecord = (TextRecord) object;
- TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
- text.setText(textRecord.getText());
- list.addView(text);
- } else if (object instanceof UriRecord) {
- UriRecord uriRecord = (UriRecord) object;
- TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
- text.setText(uriRecord.getUri().toString());
- list.addView(text);
- } else if (object instanceof SmartPoster) {
- TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
- SmartPoster poster = (SmartPoster) object;
- TextRecord title = poster.getTitle();
- if (title != null) {
- text.setText(title.getText());
- }
- list.addView(text);
- }
- }
+ ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msg);
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
+ text.setText(parsedMsg.getSnippet(Locale.getDefault()));
+ list.addView(text);
}
}
--- /dev/null
+/*
+ * 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.message;
+
+import java.util.Locale;
+
+/**
+ * A parsed message containing no elements.
+ */
+class EmptyMessage implements ParsedNdefMessage {
+
+ /* package private */ EmptyMessage() { }
+
+ @Override
+ public String getSnippet(Locale locale) {
+ return "Empty Tag"; // TODO: localize
+ }
+
+ @Override
+ public boolean isStarred() {
+ return false;
+ }
+}
--- /dev/null
+/*
+ * 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.message;
+
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+
+import com.android.apps.tag.record.ParsedNdefRecord;
+import com.android.apps.tag.record.SmartPoster;
+import com.android.apps.tag.record.TextRecord;
+import com.android.apps.tag.record.UriRecord;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for creating {@link ParsedNdefMessage}s.
+ */
+public class NdefMessageParser {
+
+ // Utility class
+ private NdefMessageParser() { }
+
+ /** Parse an NdefMessage */
+ public static ParsedNdefMessage parse(NdefMessage message) {
+ List<ParsedNdefRecord> elements = getRecords(message);
+
+ if (elements.isEmpty()) {
+ return new EmptyMessage();
+ }
+
+ ParsedNdefRecord first = elements.get(0);
+
+ if (elements.size() == 1) {
+ if (first instanceof SmartPoster) {
+ return new SmartPosterMessage((SmartPoster) first);
+ }
+ if (first instanceof TextRecord) {
+ return new TextMessage((TextRecord) first);
+ }
+ if (first instanceof UriRecord) {
+ return new UriMessage((UriRecord) first);
+ }
+ }
+
+ return new UnknownMessage(elements);
+ }
+
+ public static List<ParsedNdefRecord> getRecords(NdefMessage message) {
+ List<ParsedNdefRecord> elements = new ArrayList<ParsedNdefRecord>();
+ for (NdefRecord record : message.getRecords()) {
+ if (UriRecord.isUri(record)) {
+ elements.add(UriRecord.parse(record));
+ } else if (TextRecord.isText(record)) {
+ elements.add(TextRecord.parse(record));
+ } else if (SmartPoster.isPoster(record)) {
+ elements.add(SmartPoster.parse(record));
+ }
+ }
+ return elements;
+ }
+}
--- /dev/null
+/*
+ * 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.message;
+
+import java.util.Locale;
+
+/**
+ * A parsed version of an {@link android.nfc.NdefMessage}
+ */
+public interface ParsedNdefMessage {
+
+ /**
+ * Returns the snippet information associated with the NdefMessage
+ * most appropriate for the given {@code locale}.
+ */
+ public String getSnippet(Locale locale);
+
+ // TODO: Determine if this is the best place for holding whether
+ // the user has starred this parsed message.
+ public boolean isStarred();
+}
--- /dev/null
+/*
+ * 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.message;
+
+import com.android.apps.tag.record.SmartPoster;
+import com.android.apps.tag.record.TextRecord;
+import com.google.common.base.Preconditions;
+
+import java.util.Locale;
+
+/**
+ * A message consisting of one {@link SmartPoster} object.
+ */
+class SmartPosterMessage implements ParsedNdefMessage {
+ private final SmartPoster mPoster;
+
+ SmartPosterMessage(SmartPoster poster) {
+ mPoster = Preconditions.checkNotNull(poster);
+ }
+
+ @Override
+ public String getSnippet(Locale locale) {
+ TextRecord title = mPoster.getTitle();
+ if (title == null) {
+ return mPoster.getUriRecord().getUri().toString();
+ }
+ return title.getText();
+ }
+
+ @Override
+ public boolean isStarred() {
+ return false;
+ }
+}
--- /dev/null
+/*
+ * 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.message;
+
+import com.android.apps.tag.record.TextRecord;
+import com.google.common.base.Preconditions;
+
+import java.util.Locale;
+
+/**
+ * A message containing one text element
+ */
+class TextMessage implements ParsedNdefMessage {
+ private final TextRecord mRecord;
+
+ TextMessage(TextRecord record) {
+ mRecord = Preconditions.checkNotNull(record);
+ }
+
+ @Override
+ public String getSnippet(Locale locale) {
+ return mRecord.getText();
+ }
+
+ @Override
+ public boolean isStarred() {
+ return false;
+ }
+}
--- /dev/null
+/*
+ * 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.message;
+
+import com.android.apps.tag.record.ParsedNdefRecord;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Locale;
+
+/**
+ * The catchall parsed message format for when nothing else better applies.
+ */
+class UnknownMessage implements ParsedNdefMessage {
+
+ private final ImmutableList<ParsedNdefRecord> mRecords;
+
+ UnknownMessage(Iterable<ParsedNdefRecord> records) {
+ mRecords = ImmutableList.copyOf(records);
+ }
+
+ @Override
+ public String getSnippet(Locale locale) {
+ // TODO: localize
+ return "Unknown record type with " + mRecords.size() + " elements.";
+ }
+
+ @Override
+ public boolean isStarred() {
+ return false;
+ }
+}
--- /dev/null
+/*
+ * 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.message;
+
+import com.android.apps.tag.record.UriRecord;
+import com.google.common.base.Preconditions;
+
+import java.util.Locale;
+
+/**
+ * A {@link ParsedNdefMessage} consisting of one {@link UriRecord}.
+ */
+class UriMessage implements ParsedNdefMessage {
+
+ private final UriRecord mRecord;
+
+ UriMessage(UriRecord record) {
+ mRecord = Preconditions.checkNotNull(record);
+ }
+
+ @Override
+ public String getSnippet(Locale locale) {
+ // URIs cannot be localized
+ return mRecord.getUri().toString();
+ }
+
+ @Override
+ public boolean isStarred() {
+ return false;
+ }
+}
package com.android.apps.tag.record;
-import com.android.apps.tag.NdefUtil;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
-import java.util.Arrays;
+import com.android.apps.tag.message.NdefMessageParser;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
import javax.annotation.Nullable;
/**
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
try {
NdefMessage subRecords = new NdefMessage(record.getPayload());
- UriRecord uri = Iterables.getOnlyElement(NdefUtil.getUris(subRecords));
- Iterable<TextRecord> textFields = NdefUtil.getTextFields(subRecords);
+
+ Iterable<ParsedNdefRecord> records = NdefMessageParser.getRecords(subRecords);
+
+ UriRecord uri = Iterables.getOnlyElement(Iterables.filter(records, UriRecord.class));
+ Iterable<TextRecord> textFields = Iterables.filter(records, TextRecord.class);
+
TextRecord title = null;
if (!Iterables.isEmpty(textFields)) {
title = Iterables.get(textFields, 0);
return new SmartPoster(uri, title);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
+ } catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(e);
}
}
package com.android.apps.tag.record;
import android.nfc.NdefRecord;
+
import com.google.common.base.Preconditions;
import java.io.UnsupportedEncodingException;
*/
public class TextRecord implements ParsedNdefRecord {
+ /** ISO/IANA language code */
private final String mLanguageCode;
private final String mText;
return mText;
}
+ /**
+ * Returns the ISO/IANA language code associated with this text element.
+ *
+ * TODO: this should return a {@link java.util.Locale}
+ */
public String getLanguageCode() {
return mLanguageCode;
}
import android.net.Uri;
import android.nfc.NdefRecord;
+
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
package com.android.apps.tag;
+import android.nfc.NdefRecord;
import android.test.AndroidTestCase;
+
import com.android.apps.tag.record.TextRecord;
import com.google.common.primitives.Bytes;
-import android.nfc.NdefRecord;
import java.io.UnsupportedEncodingException;
package com.android.apps.tag;
-import android.test.AndroidTestCase;
import android.nfc.NdefMessage;
+import android.test.AndroidTestCase;
+
import com.android.apps.tag.record.SmartPoster;
/**