diff --git a/apps/Tag/Android.mk b/apps/Tag/Android.mk
index 949cd3f6b..f069cd859 100644
--- a/apps/Tag/Android.mk
+++ b/apps/Tag/Android.mk
@@ -3,11 +3,12 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := guava
+
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := TagApp
-# LOCAL_PROGUARD_EABLED := disabled
# LOCAL_SDK_VERSION := current
diff --git a/apps/Tag/AndroidManifest.xml b/apps/Tag/AndroidManifest.xml
index 3388a3031..13fedddd8 100644
--- a/apps/Tag/AndroidManifest.xml
+++ b/apps/Tag/AndroidManifest.xml
@@ -31,6 +31,7 @@
+
diff --git a/apps/Tag/src/com/android/apps/tag/NdefUtil.java b/apps/Tag/src/com/android/apps/tag/NdefUtil.java
new file mode 100644
index 000000000..f2b01bbbe
--- /dev/null
+++ b/apps/Tag/src/com/android/apps/tag/NdefUtil.java
@@ -0,0 +1,150 @@
+/*
+ * 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;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.primitives.Bytes;
+import com.trustedlogic.trustednfc.android.NdefRecord;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charsets;
+import java.util.Arrays;
+
+/**
+ * Utilities for dealing with conversions to and from NdefRecords.
+ *
+ * TODO: Possibly move this class into core Android.
+ */
+public class NdefUtil {
+ private static final byte[] EMPTY = new byte[0];
+
+ /**
+ * NFC Forum "URI Record Type Definition"
+ *
+ * This is a mapping of "URI Identifier Codes" to URI string prefixes,
+ * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
+ */
+ private static final
+ BiMap URI_PREFIX_MAP = ImmutableBiMap.builder()
+ .put((byte) 0x00, "")
+ .put((byte) 0x01, "http://www.")
+ .put((byte) 0x02, "https://www.")
+ .put((byte) 0x03, "http://")
+ .put((byte) 0x04, "https://")
+ .put((byte) 0x05, "tel:")
+ .put((byte) 0x06, "mailto:")
+ .put((byte) 0x07, "ftp://anonymous:anonymous@")
+ .put((byte) 0x08, "ftp://ftp.")
+ .put((byte) 0x09, "ftps://")
+ .put((byte) 0x0A, "sftp://")
+ .put((byte) 0x0B, "smb://")
+ .put((byte) 0x0C, "nfs://")
+ .put((byte) 0x0D, "ftp://")
+ .put((byte) 0x0E, "dav://")
+ .put((byte) 0x0F, "news:")
+ .put((byte) 0x10, "telnet://")
+ .put((byte) 0x11, "imap:")
+ .put((byte) 0x12, "rtsp://")
+ .put((byte) 0x13, "urn:")
+ .put((byte) 0x14, "pop:")
+ .put((byte) 0x15, "sip:")
+ .put((byte) 0x16, "sips:")
+ .put((byte) 0x17, "tftp:")
+ .put((byte) 0x18, "btspp://")
+ .put((byte) 0x19, "btl2cap://")
+ .put((byte) 0x1A, "btgoep://")
+ .put((byte) 0x1B, "tcpobex://")
+ .put((byte) 0x1C, "irdaobex://")
+ .put((byte) 0x1D, "file://")
+ .put((byte) 0x1E, "urn:epc:id:")
+ .put((byte) 0x1F, "urn:epc:tag:")
+ .put((byte) 0x20, "urn:epc:pat:")
+ .put((byte) 0x21, "urn:epc:raw:")
+ .put((byte) 0x22, "urn:epc:")
+ .put((byte) 0x23, "urn:nfc:")
+ .build();
+
+ /**
+ * Create a new {@link NdefRecord} containing the supplied {@link URI}.
+ */
+ public static NdefRecord toUriRecord(URI uri) {
+ byte[] uriBytes = uri.toString().getBytes(Charsets.UTF_8);
+
+ /*
+ * We prepend 0x00 to the bytes of the URI to indicate that this
+ * is the entire URI, and we are not taking advantage of the
+ * URI shortening rules in the NFC Forum URI spec section 3.2.2.
+ * This produces a NdefRecord which is slightly larger than
+ * necessary.
+ *
+ * In the future, we should use the URI shortening rules in 3.2.2
+ * to create a smaller NdefRecord.
+ */
+ byte[] payload = Bytes.concat(new byte[] { 0x00 }, uriBytes);
+
+ return new NdefRecord(NdefRecord.TNF_WELL_KNOWN_TYPE,
+ NdefRecord.TYPE_URI, EMPTY, payload);
+ }
+
+ /**
+ * Convert {@link NdefRecord} into a {@link URI}.
+ *
+ * TODO: This class does not handle NdefRecords where the TNF
+ * (Type Name Format) of the class is {@link NdefRecord#TNF_ABSOLUTE_URI}.
+ * This should be fixed.
+ *
+ * @throws URISyntaxException if the {@code NdefRecord} contains an
+ * invalid URI.
+ * @throws IllegalArgumentException if the NdefRecord is not a
+ * record containing a URI.
+ */
+ public static URI toURI(NdefRecord record) throws URISyntaxException {
+ Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN_TYPE);
+ Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.TYPE_URI));
+
+ byte[] payload = record.getPayload();
+
+ /*
+ * payload[0] contains the URI Identifier Code, per the
+ * NFC Forum "URI Record Type Definition" section 3.2.2.
+ *
+ * payload[1]...payload[payload.length - 1] contains the rest of
+ * the URI.
+ */
+
+ String prefix = URI_PREFIX_MAP.get(payload[0]);
+ byte[] fullUri = Bytes.concat(
+ prefix.getBytes(Charsets.UTF_8),
+ Arrays.copyOfRange(payload, 1, payload.length - 1));
+
+ return new URI(new String(fullUri, Charsets.UTF_8));
+ }
+
+ public static boolean isURI(NdefRecord record) {
+ try {
+ toURI(record);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+}
diff --git a/apps/Tag/src/com/android/apps/tag/SaveTag.java b/apps/Tag/src/com/android/apps/tag/SaveTag.java
new file mode 100644
index 000000000..877f9a711
--- /dev/null
+++ b/apps/Tag/src/com/android/apps/tag/SaveTag.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.widget.Toast;
+
+/**
+ * @author nnk@google.com (Nick Kralevich)
+ */
+public class SaveTag extends Activity implements DialogInterface.OnClickListener {
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ showDialog(1);
+ String s = getIntent().getExtras().toString();
+ Toast.makeText(this.getBaseContext(), "SaveTag: " + s, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle args) {
+ return new AlertDialog.Builder(this)
+ .setTitle("Welcome! T2000 Festival")
+ .setPositiveButton("Save", this)
+ .setNegativeButton("Cancel", this)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+}
diff --git a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
index 15c18f3ba..a105d5c78 100644
--- a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
+++ b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
@@ -19,6 +19,13 @@ package com.android.apps.tag;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import com.google.common.annotations.VisibleForTesting;
+import com.trustedlogic.trustednfc.android.NdefMessage;
+import com.trustedlogic.trustednfc.android.NdefRecord;
+
+import java.net.URI;
+import java.util.Date;
/**
* @author nnk@google.com (Nick Kralevich)
@@ -26,28 +33,51 @@ import android.database.sqlite.SQLiteOpenHelper;
public class TagDBHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
- private static final String TABLE_CREATE = "create table Tags ("
- + "_id INTEGER PRIMARY KEY ASC, "
- + "description TEXT, "
- + "date TEXT"
+
+ private static final String NDEF_MSG = "create table NdefMessage ("
+ + "_id INTEGER NOT NULL, "
+ + "bytes TEXT NOT NULL, " // TODO: This should be a blob
+ + "date TEXT NOT NULL, "
+ + "PRIMARY KEY(_id)"
+ ")";
- private static final String FAKE_DATA =
- "INSERT INTO Tags (description) values ('hello world')";
-
- private static final String FAKE_DATA2 =
- "INSERT INTO Tags (description) values ('hi world')";
+ private static final String INSERT =
+ "INSERT INTO NdefMessage (bytes, date) values (?, ?)";
public TagDBHelper(Context context) {
- super(context, "Tags.db", null, DATABASE_VERSION);
+ this(context, "Tags.db");
+ }
+
+ @VisibleForTesting
+ public TagDBHelper(Context context, String dbFile) {
+ super(context, dbFile, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL(TABLE_CREATE);
- db.execSQL(FAKE_DATA);
- db.execSQL(FAKE_DATA2);
+ db.execSQL(NDEF_MSG);
+
+ // A fake message containing 1 URL
+ NdefMessage msg1 = new NdefMessage(new NdefRecord[] {
+ NdefUtil.toUriRecord(URI.create("http://www.google.com"))
+ });
+
+ // A fake message containing 2 URLs
+ NdefMessage msg2 = new NdefMessage(new NdefRecord[] {
+ NdefUtil.toUriRecord(URI.create("http://www.youtube.com")),
+ NdefUtil.toUriRecord(URI.create("http://www.android.com"))
+ });
+
+ insert(db, msg1);
+ insert(db, msg2);
+ }
+
+ private void insert(SQLiteDatabase db, NdefMessage msg) {
+ SQLiteStatement stmt = db.compileStatement(INSERT);
+ stmt.bindString(1, new String(msg.toByteArray())); // TODO: This should be a blob
+ stmt.bindString(2, new Date().toString());
+ stmt.executeInsert();
}
@Override
diff --git a/apps/Tag/src/com/android/apps/tag/TagList.java b/apps/Tag/src/com/android/apps/tag/TagList.java
index f8a93e692..daaa9c47e 100644
--- a/apps/Tag/src/com/android/apps/tag/TagList.java
+++ b/apps/Tag/src/com/android/apps/tag/TagList.java
@@ -34,24 +34,24 @@ import android.widget.Toast;
*/
public class TagList extends ListActivity implements DialogInterface.OnClickListener {
+ private SQLiteDatabase db;
+ private Cursor cursor;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(getBaseContext(), "entered method", Toast.LENGTH_SHORT).show();
- SQLiteDatabase db = new TagDBHelper(this.getBaseContext()).getReadableDatabase();
- Cursor c = db.query("Tags", new String[] { "_id", "description" }, null, null, null, null, null);
+ db = new TagDBHelper(getBaseContext()).getReadableDatabase();
+ cursor = db.query("NdefMessage", new String[] { "_id", "bytes" }, null, null, null, null, null);
SimpleCursorAdapter sca =
new SimpleCursorAdapter(this,
android.R.layout.two_line_list_item,
- c,
- new String[] { "description" },
+ cursor,
+ new String[] { "bytes" },
new int[] { android.R.id.text1 });
setListAdapter(sca);
registerForContextMenu(getListView());
- c.close();
- db.close();
Toast.makeText(getBaseContext(), "exit method", Toast.LENGTH_SHORT).show();
}
@@ -73,6 +73,17 @@ public class TagList extends ListActivity implements DialogInterface.OnClickList
.create();
}
+ @Override
+ protected void onDestroy() {
+ if (cursor != null) {
+ cursor.close();
+ }
+ if (db != null) {
+ db.close();
+ }
+ super.onDestroy();
+ }
+
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
showDialog(1);
diff --git a/apps/Tag/src/com/android/apps/tag/TagUi.java b/apps/Tag/src/com/android/apps/tag/TagUi.java
deleted file mode 100644
index 98f384244..000000000
--- a/apps/Tag/src/com/android/apps/tag/TagUi.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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;
-
-
-/**
- * @author nnk@google.com (Nick Kralevich)
- */
-public class TagUi {
-
-
-}
diff --git a/apps/Tag/src/com/android/apps/tag/Tags.java b/apps/Tag/src/com/android/apps/tag/Tags.java
index 781e05e21..d9fa2e79e 100644
--- a/apps/Tag/src/com/android/apps/tag/Tags.java
+++ b/apps/Tag/src/com/android/apps/tag/Tags.java
@@ -32,6 +32,10 @@ public class Tags extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // While we're doing development, delete the database every time we start.
+ getBaseContext().getDatabasePath("Tags.db").delete();
+
setContentView(R.layout.main);
Resources res = getResources();
diff --git a/apps/Tag/tests/src/com/android/apps/tag/TagBroadcastReceiverTest.java b/apps/Tag/tests/src/com/android/apps/tag/TagBroadcastReceiverTest.java
deleted file mode 100644
index e030b6aae..000000000
--- a/apps/Tag/tests/src/com/android/apps/tag/TagBroadcastReceiverTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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;
-
-import android.content.Intent;
-import android.test.ActivityInstrumentationTestCase2;
-
-/**
- * @author nnk@google.com (Nick Kralevich)
- */
-public class TagBroadcastReceiverTest extends ActivityInstrumentationTestCase2 {
- /**
- * Creates an {@link ActivityInstrumentationTestCase2} for the {@link Tags} activity.
- */
- public TagBroadcastReceiverTest() {
- super(Tags.class);
- }
-
- public void testWrongMessage() {
- TagBroadcastReceiver receiver = new TagBroadcastReceiver();
- Intent i = new Intent().setAction("BOGUS");
- receiver.onReceive(getActivity().getBaseContext(), i);
- assertDatabaseNoChange(receiver);
- }
-
- private void assertDatabaseNoChange(TagBroadcastReceiver receiver) {
- // TODO: implement
- }
-
-}