getUris(NdefMessage message) {
+ return Iterables.filter(getObjects(message), Uri.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
+ * {@link String}s corresponding to NDEF text records, or {@link Uri}s
* corresponding to NDEF URI records.
*
* TODO: Is this API too generic? Should we keep it?
*/
- private static Iterable getObjects(NdefMessage message) {
- try {
- List retval = new ArrayList();
- for (NdefRecord record : message.getRecords()) {
- if (isURI(record)) {
- retval.add(toURI(record));
- } else if (isText(record)) {
- retval.add(toText(record));
- } else if (SmartPoster.isPoster(record)) {
- retval.add(SmartPoster.from(record));
- }
+ public static Iterable getObjects(NdefMessage message) {
+ List retval = new ArrayList();
+ for (NdefRecord record : message.getRecords()) {
+ if (isUri(record)) {
+ retval.add(toUri(record));
+ } else if (isText(record)) {
+ retval.add(toText(record));
+ } else if (SmartPoster.isPoster(record)) {
+ retval.add(SmartPoster.from(record));
}
- return retval;
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException(e);
}
+ return retval;
}
}
diff --git a/apps/Tag/src/com/android/apps/tag/SaveTag.java b/apps/Tag/src/com/android/apps/tag/SaveTag.java
deleted file mode 100644
index 6a5f66fcc..000000000
--- a/apps/Tag/src/com/android/apps/tag/SaveTag.java
+++ /dev/null
@@ -1,96 +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.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.nfc.NdefMessage;
-import android.nfc.NdefTag;
-import android.nfc.NfcAdapter;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-/**
- * An {@code Activity} which handles a broadcast of a new tag that the device just discovered.
- */
-public class SaveTag extends Activity implements DialogInterface.OnClickListener {
- private static final String TAG = "SaveTag";
-
- @Override
- protected void onStart() {
- super.onStart();
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND
- );
-
- showDialog(1);
- NdefTag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
- NdefMessage[] msgs = tag.getNdefMessages();
-
- if (msgs.length == 0) {
- Log.d(TAG, "No NDEF messages");
- return;
- }
- if (msgs.length > 1) {
- Log.d(TAG, "Multiple NDEF messages, only saving first");
- }
- String s = toHexString(msgs[0].toByteArray());
- Log.d("SaveTag", s);
- 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();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- dismissDialog(1);
- }
-
- private static final char[] hexDigits = "0123456789abcdef".toCharArray();
-
- private static String toHexString(byte[] bytes) {
- StringBuilder sb = new StringBuilder(3 * bytes.length);
- for (byte b : bytes) {
- sb.append("(byte) 0x")
- .append(hexDigits[(b >> 4) & 0xf])
- .append(hexDigits[b & 0xf]).append(", ");
- }
- return sb.toString();
- }
-}
diff --git a/apps/Tag/src/com/android/apps/tag/SmartPoster.java b/apps/Tag/src/com/android/apps/tag/SmartPoster.java
index 1e107235b..dd6f518fb 100644
--- a/apps/Tag/src/com/android/apps/tag/SmartPoster.java
+++ b/apps/Tag/src/com/android/apps/tag/SmartPoster.java
@@ -18,13 +18,15 @@ package com.android.apps.tag;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
+
+import android.net.Uri;
+import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
-import android.nfc.FormatException;
+
+import java.util.Arrays;
import javax.annotation.Nullable;
-import java.net.URI;
-import java.util.Arrays;
/**
* A representation of an NFC Forum "Smart Poster".
@@ -39,7 +41,7 @@ public class SmartPoster {
* This record is optional."
*/
- private final String titleRecord;
+ private final String mTitleRecord;
/**
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
@@ -48,22 +50,22 @@ public class SmartPoster {
* records are just metadata about this record. There MUST be one URI
* record and there MUST NOT be more than one."
*/
- private final URI uriRecord;
+ private final Uri mUriRecord;
- private SmartPoster(URI uri, @Nullable String title) {
- uriRecord = Preconditions.checkNotNull(uri);
- titleRecord = title;
+ private SmartPoster(Uri uri, @Nullable String title) {
+ mUriRecord = Preconditions.checkNotNull(uri);
+ mTitleRecord = title;
}
- public URI getURI() {
- return uriRecord;
+ public Uri getUri() {
+ return mUriRecord;
}
/**
- * Returns the title of the smartposter. This may be {@code null}.
+ * Returns the title of the smart poster. This may be {@code null}.
*/
public String getTitle() {
- return titleRecord;
+ return mTitleRecord;
}
public static SmartPoster from(NdefRecord record) {
@@ -71,7 +73,7 @@ public class SmartPoster {
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
try {
NdefMessage subRecords = new NdefMessage(record.getPayload());
- URI uri = Iterables.getOnlyElement(NdefUtil.getURIs(subRecords));
+ Uri uri = Iterables.getOnlyElement(NdefUtil.getUris(subRecords));
Iterable textFields = NdefUtil.getTextFields(subRecords);
String title = null;
if (!Iterables.isEmpty(textFields)) {
diff --git a/apps/Tag/src/com/android/apps/tag/TagAdapter.java b/apps/Tag/src/com/android/apps/tag/TagAdapter.java
new file mode 100644
index 000000000..f69c3ea4a
--- /dev/null
+++ b/apps/Tag/src/com/android/apps/tag/TagAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.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.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.CursorAdapter;
+import android.widget.TextView;
+
+/**
+ * A custom {@link Adapter} that renders tag entries for a list.
+ */
+public class TagAdapter extends CursorAdapter {
+
+ private final LayoutInflater mInflater;
+
+ public TagAdapter(Context context) {
+ super(context, null, false);
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView mainLine = (TextView) view.findViewById(R.id.title);
+ TextView dateLine = (TextView) view.findViewById(R.id.date);
+
+ NdefMessage msg = null;
+ try {
+ msg = new NdefMessage(cursor.getBlob(cursor.getColumnIndex(NdefMessagesTable.BYTES)));
+ } catch (FormatException e) {
+ Log.e("foo", "poorly formatted message", e);
+ }
+
+ if (msg == null) {
+ mainLine.setText("Invalid tag");
+ } else {
+ try {
+ SmartPoster poster = SmartPoster.from(msg.getRecords()[0]);
+ mainLine.setText(poster.getTitle());
+ } catch (IllegalArgumentException e) {
+ // Not a smart poster
+ NdefRecord record = msg.getRecords()[0];
+ Uri uri = null;
+ try {
+ uri = NdefUtil.toUri(record);
+ mainLine.setText(uri.toString());
+ } catch (IllegalArgumentException e2) {
+ mainLine.setText("Not a smart poster or URL");
+ }
+ }
+ }
+ dateLine.setText(DateUtils.getRelativeTimeSpanString(
+ context, cursor.getLong(cursor.getColumnIndex(NdefMessagesTable.DATE))));
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.tag_list_item, null);
+ }
+}
diff --git a/apps/Tag/src/com/android/apps/tag/TagBroadcastReceiver.java b/apps/Tag/src/com/android/apps/tag/TagBroadcastReceiver.java
deleted file mode 100644
index b5ef1e740..000000000
--- a/apps/Tag/src/com/android/apps/tag/TagBroadcastReceiver.java
+++ /dev/null
@@ -1,43 +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.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.NdefTag;
-
-import android.nfc.NdefMessage;
-import android.nfc.NfcAdapter;
-
-/**
- * When we receive a new NDEF tag, start the activity to
- * process the tag.
- */
-public class TagBroadcastReceiver extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(NfcAdapter.ACTION_NDEF_TAG_DISCOVERED)) {
- NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- Intent i = new Intent(context, SaveTag.class)
- .putExtra(NfcAdapter.EXTRA_TAG, tag)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
- }
-}
diff --git a/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java b/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
index a4cbb86a6..38d3bfab8 100644
--- a/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
+++ b/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
@@ -16,6 +16,7 @@
package com.android.apps.tag;
+import android.app.Activity;
import android.app.TabActivity;
import android.content.Intent;
import android.content.res.Resources;
@@ -23,34 +24,27 @@ import android.os.Bundle;
import android.widget.TabHost;
/**
- * A browsing {@code Activity} that displays the saved tags in categories under tabs.
+ * A browsing {@link Activity} that displays the saved tags in categories under tabs.
*/
public class TagBrowserActivity 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();
TabHost tabHost = getTabHost();
- Intent i = new Intent().setClass(this, TagList.class);
- Intent iSavedList = new Intent().setClass(this, TagList.class)
- .putExtra(TagList.SHOW_SAVED_ONLY, true);
- Intent iRecentList = new Intent().setClass(this, TagList.class);
-
- TabHost.TabSpec spec1 = tabHost.newTabSpec("1")
- .setIndicator("Saved", res.getDrawable(R.drawable.ic_menu_tag))
- .setContent(iSavedList);
+ TabHost.TabSpec spec1 = tabHost.newTabSpec("saved")
+ .setIndicator(getText(R.string.tab_saved), res.getDrawable(R.drawable.ic_menu_tag))
+ .setContent(new Intent().setClass(this, TagList.class)
+ .putExtra(TagList.EXTRA_SHOW_SAVED_ONLY, true));
tabHost.addTab(spec1);
- TabHost.TabSpec spec2 = tabHost.newTabSpec("2")
- .setIndicator("Recent", res.getDrawable(R.drawable.ic_menu_desk_clock))
- .setContent(iRecentList);
+ TabHost.TabSpec spec2 = tabHost.newTabSpec("recent")
+ .setIndicator(getText(R.string.tab_recent), res.getDrawable(R.drawable.ic_menu_desk_clock))
+ .setContent(new Intent().setClass(this, TagList.class));
tabHost.addTab(spec2);
}
}
diff --git a/apps/Tag/src/com/android/apps/tag/TagCursorAdapter.java b/apps/Tag/src/com/android/apps/tag/TagCursorAdapter.java
deleted file mode 100644
index a658268e6..000000000
--- a/apps/Tag/src/com/android/apps/tag/TagCursorAdapter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-
-package com.android.apps.tag;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.text.format.DateUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Adapter;
-import android.widget.CursorAdapter;
-import android.widget.TextView;
-
-/**
- * A custom {@link Adapter} that renders tag entries for a list.
- */
-public class TagCursorAdapter extends CursorAdapter {
-
- private final LayoutInflater mInflater;
-
- public TagCursorAdapter(Context context, Cursor c) {
- super(context, c);
-
- mInflater = LayoutInflater.from(context);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView mainLine = (TextView) view.findViewById(R.id.title);
- TextView dateLine = (TextView) view.findViewById(R.id.date);
-
- // TODO(benkomalo): either write a cursor abstraction, or use constants for column indices.
- mainLine.setText(cursor.getString(cursor.getColumnIndex("bytes")));
- dateLine.setText(DateUtils.getRelativeTimeSpanString(
- context, cursor.getLong(cursor.getColumnIndex("date"))));
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.tag_list_item, null);
- }
-}
diff --git a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
index 06fa9e001..654cb47f6 100644
--- a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
+++ b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
@@ -22,34 +22,32 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
+import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
-import java.net.URI;
-import java.util.Date;
-
/**
* Database utilities for the saved tags.
*/
public class TagDBHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 1;
+ private static final String DATABASE_NAME = "tags.db";
+ private static final int DATABASE_VERSION = 3;
- private static final String NDEF_MSG = "create table NdefMessage ("
- + "_id INTEGER NOT NULL, "
- + "bytes BLOB NOT NULL, "
- + "date INTEGER NOT NULL, "
- + "saved TEXT NOT NULL default 0," // boolean
- + "PRIMARY KEY(_id)"
- + ")";
-
- private static final String INSERT =
- "INSERT INTO NdefMessage (bytes, date, saved) values (?, ?, ?)";
+ public interface NdefMessagesTable {
+ public static final String TABLE_NAME = "nedf_msg";
+ public static final String _ID = "_id";
+ public static final String TITLE = "title";
+ public static final String BYTES = "bytes";
+ public static final String DATE = "date";
+ public static final String SAVED = "saved";
+ }
+
/**
* A real NFC tag containing an NFC "smart poster". This smart poster
- * consists of the text "NFC Forum Type 4 Tag" in english combined with
+ * consists of the text "NFC Forum Type 4 Tag" in English combined with
* the URL "http://www.nxp.com/nfc"
*/
public static final byte[] REAL_NFC_MSG = new byte[] {
@@ -115,53 +113,85 @@ public class TagDBHelper extends SQLiteOpenHelper {
// end smart poster payload
};
- public TagDBHelper(Context context) {
- this(context, "Tags.db");
+ private static TagDBHelper sInstance;
+
+ public static synchronized TagDBHelper getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new TagDBHelper(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ private TagDBHelper(Context context) {
+ this(context, DATABASE_NAME);
}
@VisibleForTesting
- public TagDBHelper(Context context, String dbFile) {
+ TagDBHelper(Context context, String dbFile) {
super(context, dbFile, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL(NDEF_MSG);
+ db.execSQL("CREATE TABLE " + NdefMessagesTable.TABLE_NAME + " (" +
+ NdefMessagesTable._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ NdefMessagesTable.TITLE + " TEXT NOT NULL DEFAULT ''," +
+ NdefMessagesTable.BYTES + " BLOB NOT NULL, " +
+ NdefMessagesTable.DATE + " INTEGER NOT NULL, " +
+ NdefMessagesTable.SAVED + " INTEGER NOT NULL DEFAULT 0" + // boolean
+ ");");
+ db.execSQL("CREATE INDEX msgIndex ON " + NdefMessagesTable.TABLE_NAME + " (" +
+ NdefMessagesTable.DATE + " DESC, " +
+ NdefMessagesTable.SAVED + " ASC" +
+ ")");
+
+ addTestData(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // Drop everything and recreate it for now
+ db.execSQL("DROP TABLE IF EXISTS " + NdefMessagesTable.TABLE_NAME);
+ onCreate(db);
+ }
+
+ private void addTestData(SQLiteDatabase db) {
// A fake message containing 1 URL
NdefMessage msg1 = new NdefMessage(new NdefRecord[] {
- NdefUtil.toUriRecord(URI.create("http://www.google.com"))
+ NdefUtil.toUriRecord(Uri.parse("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"))
+ NdefUtil.toUriRecord(Uri.parse("http://www.youtube.com")),
+ NdefUtil.toUriRecord(Uri.parse("http://www.android.com"))
});
- insert(db, msg1, false);
- insert(db, msg2, true);
+ insertNdefMessage(db, msg1, false);
+ insertNdefMessage(db, msg2, true);
try {
// A real message obtained from an NFC Forum Type 4 tag.
NdefMessage msg3 = new NdefMessage(REAL_NFC_MSG);
- insert(db, msg3, false);
+ insertNdefMessage(db, msg3, false);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
- private void insert(SQLiteDatabase db, NdefMessage msg, boolean isSaved) {
- SQLiteStatement stmt = db.compileStatement(INSERT);
- stmt.bindString(1, new String(msg.toByteArray())); // TODO: This should be a blob
- stmt.bindLong(2, System.currentTimeMillis());
- String isSavedStr = isSaved ? "1" : "0";
- stmt.bindString(3, isSavedStr);
- stmt.executeInsert();
- stmt.close();
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ public void insertNdefMessage(SQLiteDatabase db, NdefMessage msg, boolean isSaved) {
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT INTO " + NdefMessagesTable.TABLE_NAME +
+ "(" + NdefMessagesTable.BYTES + ", " + NdefMessagesTable.DATE + ", " +
+ NdefMessagesTable.SAVED + ") values (?, ?, ?)");
+ stmt.bindBlob(1, msg.toByteArray());
+ stmt.bindLong(2, System.currentTimeMillis());
+ stmt.bindLong(3, isSaved ? 1 : 0);
+ stmt.executeInsert();
+ } finally {
+ if (stmt != null) stmt.close();
+ }
}
}
diff --git a/apps/Tag/src/com/android/apps/tag/TagList.java b/apps/Tag/src/com/android/apps/tag/TagList.java
index 369ef6557..45f6f6577 100644
--- a/apps/Tag/src/com/android/apps/tag/TagList.java
+++ b/apps/Tag/src/com/android/apps/tag/TagList.java
@@ -16,42 +16,47 @@
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.app.ListActivity;
import android.content.DialogInterface;
+import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;
-import android.widget.SimpleCursorAdapter;
/**
- * An {@code Activity} that displays a flat list of tags that can be "opened".
+ * An {@link Activity} that displays a flat list of tags that can be "opened".
*/
public class TagList extends ListActivity implements DialogInterface.OnClickListener {
- private SQLiteDatabase db;
- private Cursor cursor;
- static final String SHOW_SAVED_ONLY = "show_saved_only";
+ static final String TAG = "TagList";
+
+ static final String EXTRA_SHOW_SAVED_ONLY = "show_saved_only";
+
+ SQLiteDatabase mDatabase;
+ TagAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- boolean showSavedOnly = getIntent().getBooleanExtra(SHOW_SAVED_ONLY, false);
- db = new TagDBHelper(getBaseContext()).getReadableDatabase();
- String selection = showSavedOnly ? "saved=1" : null;
+ boolean showSavedOnly = getIntent().getBooleanExtra(EXTRA_SHOW_SAVED_ONLY, false);
+ mDatabase = TagDBHelper.getInstance(this).getReadableDatabase();
+ String selection = showSavedOnly ? NdefMessagesTable.SAVED + "=1" : null;
- // TODO: Use an AsyncQueryHandler so that DB queries are not done on UI thread.
- cursor = db.query(
- "NdefMessage",
- new String[] { "_id", "bytes", "date" },
- selection,
- null, null, null, null);
-
- setListAdapter(new TagCursorAdapter(this, cursor));
+ new TagLoaderTask().execute(selection);
+ mAdapter = new TagAdapter(this);
+ setListAdapter(mAdapter);
registerForContextMenu(getListView());
}
@@ -75,22 +80,53 @@ public class TagList extends ListActivity implements DialogInterface.OnClickList
@Override
protected void onDestroy() {
- if (cursor != null) {
- cursor.close();
- }
- if (db != null) {
- db.close();
+ if (mAdapter != null) {
+ mAdapter.changeCursor(null);
}
super.onDestroy();
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
- showDialog(1);
- super.onListItemClick(l, v, position, id);
+ Cursor cursor = mAdapter.getCursor();
+ cursor.moveToPosition(position);
+ byte[] tagBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(NdefMessagesTable.BYTES));
+ try {
+ NdefMessage msg = new NdefMessage(tagBytes);
+ Intent intent = new Intent(this, TagViewer.class);
+ intent.putExtra(TagViewer.EXTRA_MESSAGE, msg);
+ intent.putExtra(TagViewer.EXTRA_TAG_DB_ID, id);
+ startActivity(intent);
+ } catch (FormatException e) {
+ Log.e(TAG, "bad format for tag " + id + ": " + tagBytes, e);
+ return;
+ }
}
@Override
public void onClick(DialogInterface dialog, int which) {
}
+
+ final class TagLoaderTask extends AsyncTask {
+ @Override
+ public Cursor doInBackground(String... args) {
+ String selection = args[0];
+ Cursor cursor = mDatabase.query(
+ NdefMessagesTable.TABLE_NAME,
+ new String[] {
+ NdefMessagesTable._ID,
+ NdefMessagesTable.BYTES,
+ NdefMessagesTable.DATE,
+ NdefMessagesTable.TITLE },
+ selection,
+ null, null, null, null);
+ cursor.getCount();
+ return cursor;
+ }
+
+ @Override
+ protected void onPostExecute(Cursor cursor) {
+ mAdapter.changeCursor(cursor);
+ }
+ }
}
diff --git a/apps/Tag/src/com/android/apps/tag/TagViewer.java b/apps/Tag/src/com/android/apps/tag/TagViewer.java
new file mode 100644
index 000000000..21f64e161
--- /dev/null
+++ b/apps/Tag/src/com/android/apps/tag/TagViewer.java
@@ -0,0 +1,137 @@
+/*
+ * 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.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Color;
+import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefTag;
+import android.nfc.NfcAdapter;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * An {@link Activity} which handles a broadcast of a new tag that the device just discovered.
+ */
+public class TagViewer extends Activity {
+ static final String TAG = "SaveTag";
+ static final String EXTRA_TAG_DB_ID = "db_id";
+ static final String EXTRA_MESSAGE = "msg";
+
+ long mTagDatabaseId;
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ );
+
+ Intent intent = getIntent();
+ NdefMessage[] msgs = null;
+ NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (tag == null) {
+ // Maybe it came from the database?
+ mTagDatabaseId = intent.getLongExtra(EXTRA_TAG_DB_ID, -1);
+ NdefMessage msg = intent.getParcelableExtra(EXTRA_MESSAGE);
+ if (msg != null) {
+ msgs = new NdefMessage[] { msg };
+ }
+ } else {
+ msgs = tag.getNdefMessages();
+ // TODO use a service to avoid the process getting reaped during saving
+ new SaveTagTask().execute(msgs);
+ }
+
+ if (msgs == null || msgs.length == 0) {
+ Log.e(TAG, "No NDEF messages");
+ finish();
+ return;
+ }
+
+
+ LayoutInflater inflater = LayoutInflater.from(
+ new ContextThemeWrapper(this, android.R.style.Theme_Light));
+ LinearLayout list = (LinearLayout) inflater.inflate(R.layout.tag_viewer_list, null, false);
+ // TODO figure out why the background isn't white, the CTW should force that...
+ list.setBackgroundColor(Color.WHITE);
+ setContentView(list);
+ buildTagViews(list, inflater, msgs);
+ }
+
+ private void buildTagViews(LinearLayout list, LayoutInflater inflater, NdefMessage[] msgs) {
+ // 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 objects = NdefUtil.getObjects(msg);
+ for (Object object : objects) {
+ if (!first) {
+ list.addView(inflater.inflate(R.layout.tag_divider, list, false));
+ first = false;
+ }
+
+ if (object instanceof String) {
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
+ text.setText((CharSequence) object);
+ list.addView(text);
+ } else if (object instanceof Uri) {
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
+ text.setText(object.toString());
+ list.addView(text);
+ } else if (object instanceof SmartPoster) {
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, list, false);
+ SmartPoster poster = (SmartPoster) object;
+ text.setText(poster.getTitle());
+ list.addView(text);
+ }
+ }
+ }
+ }
+
+ final class SaveTagTask extends AsyncTask {
+ @Override
+ public Void doInBackground(NdefMessage... msgs) {
+ TagDBHelper helper = TagDBHelper.getInstance(TagViewer.this);
+ SQLiteDatabase db = helper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ for (NdefMessage msg : msgs) {
+ helper.insertNdefMessage(db, msg, false);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ return null;
+ }
+ }
+}
diff --git a/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java b/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java
index b7eb86973..e55e07d2d 100644
--- a/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java
+++ b/apps/Tag/tests/src/com/android/apps/tag/SmartPosterTest.java
@@ -28,6 +28,6 @@ public class SmartPosterTest extends AndroidTestCase {
SmartPoster poster = SmartPoster.from(msg.getRecords()[0]);
assertEquals("NFC Forum Type 4 Tag", poster.getTitle());
- assertEquals("http://www.nxp.com/nfc", poster.getURI().toString());
+ assertEquals("http://www.nxp.com/nfc", poster.getUri().toString());
}
}