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 - } - -}