diff --git a/apps/Tag/AndroidManifest.xml b/apps/Tag/AndroidManifest.xml
index 1b1e6b94c..4349180fe 100644
--- a/apps/Tag/AndroidManifest.xml
+++ b/apps/Tag/AndroidManifest.xml
@@ -23,6 +23,7 @@
package="com.android.apps.tag"
>
+
@@ -39,7 +40,7 @@
@@ -47,5 +48,7 @@
+
+
diff --git a/apps/Tag/res/drawable-hdpi/ic_menu_desk_clock.png b/apps/Tag/res/drawable-hdpi/ic_menu_desk_clock.png
deleted file mode 100644
index 0a82ad669..000000000
Binary files a/apps/Tag/res/drawable-hdpi/ic_menu_desk_clock.png and /dev/null differ
diff --git a/apps/Tag/res/drawable-hdpi/ic_tab_selected_starred.png b/apps/Tag/res/drawable-hdpi/ic_tab_selected_starred.png
new file mode 100644
index 000000000..d9182b5c7
Binary files /dev/null and b/apps/Tag/res/drawable-hdpi/ic_tab_selected_starred.png differ
diff --git a/apps/Tag/res/drawable-hdpi/ic_tab_unselected_starred.png b/apps/Tag/res/drawable-hdpi/ic_tab_unselected_starred.png
new file mode 100644
index 000000000..259d2d34a
Binary files /dev/null and b/apps/Tag/res/drawable-hdpi/ic_tab_unselected_starred.png differ
diff --git a/apps/Tag/res/drawable-mdpi/ic_menu_desk_clock.png b/apps/Tag/res/drawable-mdpi/ic_menu_desk_clock.png
deleted file mode 100644
index e4fa67196..000000000
Binary files a/apps/Tag/res/drawable-mdpi/ic_menu_desk_clock.png and /dev/null differ
diff --git a/apps/Tag/res/drawable-mdpi/ic_tab_selected_starred.png b/apps/Tag/res/drawable-mdpi/ic_tab_selected_starred.png
new file mode 100644
index 000000000..089dfe940
Binary files /dev/null and b/apps/Tag/res/drawable-mdpi/ic_tab_selected_starred.png differ
diff --git a/apps/Tag/res/drawable-mdpi/ic_tab_unselected_starred.png b/apps/Tag/res/drawable-mdpi/ic_tab_unselected_starred.png
new file mode 100644
index 000000000..32fe67df0
Binary files /dev/null and b/apps/Tag/res/drawable-mdpi/ic_tab_unselected_starred.png differ
diff --git a/apps/Tag/res/layout/tag_viewer_list.xml b/apps/Tag/res/drawable/ic_tab_starred.xml
similarity index 64%
rename from apps/Tag/res/layout/tag_viewer_list.xml
rename to apps/Tag/res/drawable/ic_tab_starred.xml
index 3d322d539..e84db4a44 100644
--- a/apps/Tag/res/layout/tag_viewer_list.xml
+++ b/apps/Tag/res/drawable/ic_tab_starred.xml
@@ -1,12 +1,12 @@
-
-
+
+
+
+
+
diff --git a/apps/Tag/res/layout/main.xml b/apps/Tag/res/layout/main.xml
index 50b2324de..3880a3abe 100644
--- a/apps/Tag/res/layout/main.xml
+++ b/apps/Tag/res/layout/main.xml
@@ -33,7 +33,6 @@
+ android:layout_height="match_parent" />
diff --git a/apps/Tag/res/layout/tag_uri.xml b/apps/Tag/res/layout/tag_uri.xml
new file mode 100644
index 000000000..3ce4b2f24
--- /dev/null
+++ b/apps/Tag/res/layout/tag_uri.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/Tag/res/layout/tag_viewer.xml b/apps/Tag/res/layout/tag_viewer.xml
new file mode 100644
index 000000000..55e2f7d7a
--- /dev/null
+++ b/apps/Tag/res/layout/tag_viewer.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/Tag/res/values/strings.xml b/apps/Tag/res/values/strings.xml
index 9b736784e..ed0525a3c 100644
--- a/apps/Tag/res/values/strings.xml
+++ b/apps/Tag/res/values/strings.xml
@@ -14,16 +14,29 @@
limitations under the License.
-->
-
-
- Hello, World!
- help and info
- Saved
+
- Recent
+ Tags
-
- Saved
+
+ Starred
+
+ Unknown tag type
+
+
+ Empty tag
+
+
+ Delete
+
+
+ To skip adding this tag to your collection, press Cancel
+
+
+ Text %s
+
+
+ Call %s
diff --git a/apps/Tag/res/menu/mailbox_menu.xml b/apps/Tag/res/values/styles.xml
similarity index 65%
rename from apps/Tag/res/menu/mailbox_menu.xml
rename to apps/Tag/res/values/styles.xml
index 8bb8d7b4d..bed590047 100644
--- a/apps/Tag/res/menu/mailbox_menu.xml
+++ b/apps/Tag/res/values/styles.xml
@@ -14,9 +14,12 @@
limitations under the License.
-->
-
+
+
+
diff --git a/apps/Tag/src/com/android/apps/tag/TagAdapter.java b/apps/Tag/src/com/android/apps/tag/TagAdapter.java
index 44757c455..8fbf06467 100644
--- a/apps/Tag/src/com/android/apps/tag/TagAdapter.java
+++ b/apps/Tag/src/com/android/apps/tag/TagAdapter.java
@@ -16,12 +16,11 @@
package com.android.apps.tag;
+import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
+
import android.content.Context;
import android.database.Cursor;
-import android.nfc.FormatException;
-import android.nfc.NdefMessage;
import android.text.format.DateUtils;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -29,12 +28,6 @@ import android.widget.Adapter;
import android.widget.CursorAdapter;
import android.widget.TextView;
-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.
*/
@@ -52,19 +45,7 @@ public class TagAdapter extends CursorAdapter {
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 {
- ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msg);
- mainLine.setText(parsedMsg.getSnippet(Locale.getDefault()));
- }
+ mainLine.setText(cursor.getString(cursor.getColumnIndex(NdefMessagesTable.TITLE)));
dateLine.setText(DateUtils.getRelativeTimeSpanString(
context, cursor.getLong(cursor.getColumnIndex(NdefMessagesTable.DATE))));
}
diff --git a/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java b/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
index 38d3bfab8..eb2111305 100644
--- a/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
+++ b/apps/Tag/src/com/android/apps/tag/TagBrowserActivity.java
@@ -18,7 +18,9 @@ package com.android.apps.tag;
import android.app.Activity;
import android.app.TabActivity;
+import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TabHost;
@@ -36,16 +38,34 @@ public class TagBrowserActivity extends TabActivity {
Resources res = getResources();
TabHost tabHost = getTabHost();
- 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.addTab(tabHost.newTabSpec("tags")
+ .setIndicator(getText(R.string.tab_tags),
+ res.getDrawable(R.drawable.ic_menu_tag))
+ .setContent(new Intent().setClass(this, TagList.class)));
- 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);
+ tabHost.addTab(tabHost.newTabSpec("starred")
+ .setIndicator(getText(R.string.tab_starred),
+ res.getDrawable(R.drawable.ic_tab_starred))
+ .setContent(new Intent().setClass(this, TagList.class)
+ .putExtra(TagList.EXTRA_SHOW_STARRED_ONLY, true)));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // Restore the last active tab
+ SharedPreferences prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);
+ getTabHost().setCurrentTabByTag(prefs.getString("tab", "tags"));
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ // Save the active tab
+ SharedPreferences.Editor edit = getSharedPreferences("prefs", Context.MODE_PRIVATE).edit();
+ edit.putString("tab", getTabHost().getCurrentTabTag());
+ edit.apply();
}
}
-
diff --git a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
index dd11d9431..9c91afb44 100644
--- a/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
+++ b/apps/Tag/src/com/android/apps/tag/TagDBHelper.java
@@ -25,6 +25,10 @@ import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
+import java.util.Locale;
+
+import com.android.apps.tag.message.NdefMessageParser;
+import com.android.apps.tag.message.ParsedNdefMessage;
import com.google.common.annotations.VisibleForTesting;
/**
@@ -33,7 +37,7 @@ import com.google.common.annotations.VisibleForTesting;
public class TagDBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "tags.db";
- private static final int DATABASE_VERSION = 3;
+ private static final int DATABASE_VERSION = 5;
public interface NdefMessagesTable {
public static final String TABLE_NAME = "nedf_msg";
@@ -42,11 +46,13 @@ public class TagDBHelper extends SQLiteOpenHelper {
public static final String TITLE = "title";
public static final String BYTES = "bytes";
public static final String DATE = "date";
- public static final String SAVED = "saved";
+ public static final String STARRED = "starred";
}
private static TagDBHelper sInstance;
+ private Context mContext;
+
public static synchronized TagDBHelper getInstance(Context context) {
if (sInstance == null) {
sInstance = new TagDBHelper(context.getApplicationContext());
@@ -56,11 +62,13 @@ public class TagDBHelper extends SQLiteOpenHelper {
private TagDBHelper(Context context) {
this(context, DATABASE_NAME);
+ mContext = context;
}
@VisibleForTesting
TagDBHelper(Context context, String dbFile) {
super(context, dbFile, null, DATABASE_VERSION);
+ mContext = context;
}
@Override
@@ -70,12 +78,12 @@ public class TagDBHelper extends SQLiteOpenHelper {
NdefMessagesTable.TITLE + " TEXT NOT NULL DEFAULT ''," +
NdefMessagesTable.BYTES + " BLOB NOT NULL, " +
NdefMessagesTable.DATE + " INTEGER NOT NULL, " +
- NdefMessagesTable.SAVED + " INTEGER NOT NULL DEFAULT 0" + // boolean
+ NdefMessagesTable.STARRED + " INTEGER NOT NULL DEFAULT 0" + // boolean
");");
db.execSQL("CREATE INDEX msgIndex ON " + NdefMessagesTable.TABLE_NAME + " (" +
NdefMessagesTable.DATE + " DESC, " +
- NdefMessagesTable.SAVED + " ASC" +
+ NdefMessagesTable.STARRED + " ASC" +
")");
addTestData(db);
@@ -114,15 +122,18 @@ public class TagDBHelper extends SQLiteOpenHelper {
}
}
- public void insertNdefMessage(SQLiteDatabase db, NdefMessage msg, boolean isSaved) {
+ public void insertNdefMessage(SQLiteDatabase db, NdefMessage msg, boolean isStarred) {
+ ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msg);
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement("INSERT INTO " + NdefMessagesTable.TABLE_NAME +
"(" + NdefMessagesTable.BYTES + ", " + NdefMessagesTable.DATE + ", " +
- NdefMessagesTable.SAVED + ") values (?, ?, ?)");
+ NdefMessagesTable.STARRED + "," + NdefMessagesTable.TITLE + ") " +
+ "values (?, ?, ?, ?)");
stmt.bindBlob(1, msg.toByteArray());
stmt.bindLong(2, System.currentTimeMillis());
- stmt.bindLong(3, isSaved ? 1 : 0);
+ stmt.bindLong(3, isStarred ? 1 : 0);
+ stmt.bindString(4, parsedMsg.getSnippet(mContext, Locale.getDefault()));
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 7d09baed1..71e3097c6 100644
--- a/apps/Tag/src/com/android/apps/tag/TagList.java
+++ b/apps/Tag/src/com/android/apps/tag/TagList.java
@@ -41,7 +41,7 @@ import com.android.apps.tag.TagDBHelper.NdefMessagesTable;
public class TagList extends ListActivity implements DialogInterface.OnClickListener {
static final String TAG = "TagList";
- static final String EXTRA_SHOW_SAVED_ONLY = "show_saved_only";
+ static final String EXTRA_SHOW_STARRED_ONLY = "show_starred_only";
SQLiteDatabase mDatabase;
TagAdapter mAdapter;
@@ -50,9 +50,9 @@ public class TagList extends ListActivity implements DialogInterface.OnClickList
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- boolean showSavedOnly = getIntent().getBooleanExtra(EXTRA_SHOW_SAVED_ONLY, false);
+ boolean showStarredOnly = getIntent().getBooleanExtra(EXTRA_SHOW_STARRED_ONLY, false);
mDatabase = TagDBHelper.getInstance(this).getReadableDatabase();
- String selection = showSavedOnly ? NdefMessagesTable.SAVED + "=1" : null;
+ String selection = showStarredOnly ? NdefMessagesTable.STARRED + "=1" : null;
new TagLoaderTask().execute(selection);
mAdapter = new TagAdapter(this);
@@ -119,7 +119,7 @@ public class TagList extends ListActivity implements DialogInterface.OnClickList
NdefMessagesTable.DATE,
NdefMessagesTable.TITLE },
selection,
- null, null, null, null);
+ null, null, null, NdefMessagesTable.DATE + " DESC");
cursor.getCount();
return cursor;
}
diff --git a/apps/Tag/src/com/android/apps/tag/TagService.java b/apps/Tag/src/com/android/apps/tag/TagService.java
new file mode 100644
index 000000000..8748a5599
--- /dev/null
+++ b/apps/Tag/src/com/android/apps/tag/TagService.java
@@ -0,0 +1,58 @@
+/*
+ * 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.app.IntentService;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.nfc.NdefMessage;
+import android.os.Parcelable;
+
+public class TagService extends IntentService {
+ public static final String EXTRA_SAVE_MSGS = "msgs";
+ public static final String EXTRA_DELETE_ID = "delete";
+
+ public TagService() {
+ super("SaveTagService");
+ }
+
+ @Override
+ public void onHandleIntent(Intent intent) {
+ TagDBHelper helper = TagDBHelper.getInstance(this);
+ SQLiteDatabase db = helper.getWritableDatabase();
+ if (intent.hasExtra(EXTRA_SAVE_MSGS)) {
+ Parcelable[] parcels = intent.getParcelableArrayExtra(EXTRA_SAVE_MSGS);
+ db.beginTransaction();
+ try {
+ for (Parcelable parcel : parcels) {
+ helper.insertNdefMessage(db, (NdefMessage) parcel, false);
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ return;
+ } else if (intent.hasExtra(EXTRA_DELETE_ID)) {
+ long id = intent.getLongExtra(EXTRA_DELETE_ID, 0);
+ db.delete(NdefMessagesTable.TABLE_NAME, NdefMessagesTable._ID + "=?",
+ new String[] { Long.toString(id) });
+ return;
+ }
+ }
+}
diff --git a/apps/Tag/src/com/android/apps/tag/TagViewer.java b/apps/Tag/src/com/android/apps/tag/TagViewer.java
index a1d890020..f31666edb 100644
--- a/apps/Tag/src/com/android/apps/tag/TagViewer.java
+++ b/apps/Tag/src/com/android/apps/tag/TagViewer.java
@@ -16,39 +16,57 @@
package com.android.apps.tag;
+import com.android.apps.tag.message.NdefMessageParser;
+import com.android.apps.tag.message.ParsedNdefMessage;
+import com.android.apps.tag.record.ParsedNdefRecord;
+
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Color;
import android.nfc.NdefMessage;
import android.nfc.NdefTag;
import android.nfc.NfcAdapter;
-import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-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.
*/
-public class TagViewer extends Activity {
+public class TagViewer extends Activity implements OnClickListener, Handler.Callback {
static final String TAG = "SaveTag";
static final String EXTRA_TAG_DB_ID = "db_id";
static final String EXTRA_MESSAGE = "msg";
+ /** This activity will finish itself in this amount of time if the user doesn't do anything. */
+ static final int ACTIVITY_TIMEOUT_MS = 10 * 1000;
+
long mTagDatabaseId;
+ ImageView mIcon;
+ TextView mTitle;
+ CheckBox mStar;
+ Button mDeleteButton;
+ Button mCancelButton;
+ NdefMessage[] mMessagesToSave = null;
@Override
- protected void onStart() {
- super.onStart();
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
@@ -58,6 +76,18 @@ public class TagViewer extends Activity {
| WindowManager.LayoutParams.FLAG_DIM_BEHIND
);
+ setContentView(R.layout.tag_viewer);
+
+ mTitle = (TextView) findViewById(R.id.title);
+ mIcon = (ImageView) findViewById(R.id.icon);
+ mStar = (CheckBox) findViewById(R.id.star);
+ mDeleteButton = (Button) findViewById(R.id.btn_delete);
+ mCancelButton = (Button) findViewById(R.id.btn_cancel);
+
+ mDeleteButton.setOnClickListener(this);
+ mCancelButton.setOnClickListener(this);
+ mIcon.setImageResource(R.drawable.ic_launcher_nfc);
+
Intent intent = getIntent();
NdefMessage[] msgs = null;
NdefTag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
@@ -68,10 +98,18 @@ public class TagViewer extends Activity {
if (msg != null) {
msgs = new NdefMessage[] { msg };
}
+
+ // Hide the text about saving the tag, it's already in the database
+ findViewById(R.id.cancel_help_text).setVisibility(View.GONE);
} else {
msgs = tag.getNdefMessages();
- // TODO use a service to avoid the process getting reaped during saving
- new SaveTagTask().execute(msgs);
+ mDeleteButton.setVisibility(View.GONE);
+
+ // Set a timer on this activity since it wasn't created by the user
+ new Handler(this).sendEmptyMessageDelayed(0, ACTIVITY_TIMEOUT_MS);
+
+ // Save the messages that were just scanned
+ mMessagesToSave = msgs;
}
if (msgs == null || msgs.length == 0) {
@@ -80,43 +118,72 @@ public class TagViewer extends Activity {
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);
+ Context contentContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
+ LayoutInflater inflater = LayoutInflater.from(contentContext);
+ LinearLayout list = (LinearLayout) findViewById(R.id.list);
+
buildTagViews(list, inflater, msgs);
+
+ if (TextUtils.isEmpty(getTitle())) {
+ // There isn't a snippet for this tag, use a default title
+ setTitle(R.string.tag_unknown);
+ }
}
private void buildTagViews(LinearLayout list, LayoutInflater inflater, NdefMessage[] msgs) {
- // The body of the dialog should use the light theme
+ if (msgs == null || msgs.length == 0) {
+ return;
+ }
// Build the views from the logical records in the messages
- for (NdefMessage msg : msgs) {
- 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);
+ NdefMessage msg = msgs[0];
+
+ // Set the title to be the snippet of the message
+ ParsedNdefMessage parsedMsg = NdefMessageParser.parse(msg);
+ setTitle(parsedMsg.getSnippet(this, Locale.getDefault()));
+
+ // Build views for all of the sub records
+ for (ParsedNdefRecord record : parsedMsg.getRecords()) {
+ list.addView(record.getView(this, inflater, list));
+ inflater.inflate(R.layout.tag_divider, list, true);
}
}
-
- 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;
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitle.setText(title);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == mDeleteButton) {
+ Intent save = new Intent(this, TagService.class);
+ save.putExtra(TagService.EXTRA_DELETE_ID, mTagDatabaseId);
+ startService(save);
+ finish();
+ } else if (view == mCancelButton) {
+ mMessagesToSave = null;
+ finish();
}
}
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mMessagesToSave != null) {
+ saveMessages(mMessagesToSave);
+ }
+ }
+
+ void saveMessages(NdefMessage[] msgs) {
+ Intent save = new Intent(this, TagService.class);
+ save.putExtra(TagService.EXTRA_SAVE_MSGS, msgs);
+ startService(save);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ finish();
+ return true;
+ }
}
diff --git a/apps/Tag/src/com/android/apps/tag/message/EmptyMessage.java b/apps/Tag/src/com/android/apps/tag/message/EmptyMessage.java
index 48f9e9f85..0bf3fe66f 100644
--- a/apps/Tag/src/com/android/apps/tag/message/EmptyMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/EmptyMessage.java
@@ -16,18 +16,26 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.R;
+import com.android.apps.tag.record.ParsedNdefRecord;
+
+import android.content.Context;
+
+import java.util.ArrayList;
import java.util.Locale;
/**
* A parsed message containing no elements.
*/
-class EmptyMessage implements ParsedNdefMessage {
+class EmptyMessage extends ParsedNdefMessage {
- /* package private */ EmptyMessage() { }
+ /* package private */ EmptyMessage() {
+ super(new ArrayList());
+ }
@Override
- public String getSnippet(Locale locale) {
- return "Empty Tag"; // TODO: localize
+ public String getSnippet(Context context, Locale locale) {
+ return context.getString(R.string.tag_empty);
}
@Override
diff --git a/apps/Tag/src/com/android/apps/tag/message/NdefMessageParser.java b/apps/Tag/src/com/android/apps/tag/message/NdefMessageParser.java
index 1823f9847..101ebcf73 100644
--- a/apps/Tag/src/com/android/apps/tag/message/NdefMessageParser.java
+++ b/apps/Tag/src/com/android/apps/tag/message/NdefMessageParser.java
@@ -47,13 +47,13 @@ public class NdefMessageParser {
if (elements.size() == 1) {
if (first instanceof SmartPoster) {
- return new SmartPosterMessage((SmartPoster) first);
+ return new SmartPosterMessage((SmartPoster) first, elements);
}
if (first instanceof TextRecord) {
- return new TextMessage((TextRecord) first);
+ return new TextMessage((TextRecord) first, elements);
}
if (first instanceof UriRecord) {
- return new UriMessage((UriRecord) first);
+ return new UriMessage((UriRecord) first, elements);
}
}
diff --git a/apps/Tag/src/com/android/apps/tag/message/ParsedNdefMessage.java b/apps/Tag/src/com/android/apps/tag/message/ParsedNdefMessage.java
index 427aae4c9..b9f521082 100644
--- a/apps/Tag/src/com/android/apps/tag/message/ParsedNdefMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/ParsedNdefMessage.java
@@ -16,20 +16,39 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.record.ParsedNdefRecord;
+import com.google.common.collect.ImmutableList;
+
+import android.content.Context;
+
+import java.util.List;
import java.util.Locale;
/**
* A parsed version of an {@link android.nfc.NdefMessage}
*/
-public interface ParsedNdefMessage {
+public abstract class ParsedNdefMessage {
+
+ private List mRecords;
+
+ public ParsedNdefMessage(List records) {
+ mRecords = ImmutableList.copyOf(records);
+ }
+
+ /**
+ * Returns the list of parsed records on this message.
+ */
+ public List getRecords() {
+ return mRecords;
+ }
/**
* Returns the snippet information associated with the NdefMessage
* most appropriate for the given {@code locale}.
*/
- public String getSnippet(Locale locale);
+ public abstract String getSnippet(Context context, Locale locale);
// TODO: Determine if this is the best place for holding whether
// the user has starred this parsed message.
- public boolean isStarred();
+ public abstract boolean isStarred();
}
diff --git a/apps/Tag/src/com/android/apps/tag/message/SmartPosterMessage.java b/apps/Tag/src/com/android/apps/tag/message/SmartPosterMessage.java
index 28cd7b3c1..b5cc74378 100644
--- a/apps/Tag/src/com/android/apps/tag/message/SmartPosterMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/SmartPosterMessage.java
@@ -16,27 +16,32 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.record.ParsedNdefRecord;
import com.android.apps.tag.record.SmartPoster;
import com.android.apps.tag.record.TextRecord;
import com.google.common.base.Preconditions;
+import android.content.Context;
+
+import java.util.List;
import java.util.Locale;
/**
* A message consisting of one {@link SmartPoster} object.
*/
-class SmartPosterMessage implements ParsedNdefMessage {
+class SmartPosterMessage extends ParsedNdefMessage {
private final SmartPoster mPoster;
- SmartPosterMessage(SmartPoster poster) {
+ SmartPosterMessage(SmartPoster poster, List records) {
+ super(Preconditions.checkNotNull(records));
mPoster = Preconditions.checkNotNull(poster);
}
@Override
- public String getSnippet(Locale locale) {
+ public String getSnippet(Context context, Locale locale) {
TextRecord title = mPoster.getTitle();
if (title == null) {
- return mPoster.getUriRecord().getUri().toString();
+ return mPoster.getUriRecord().getPrettyUriString(context);
}
return title.getText();
}
diff --git a/apps/Tag/src/com/android/apps/tag/message/TextMessage.java b/apps/Tag/src/com/android/apps/tag/message/TextMessage.java
index 3494331cf..025b6d3da 100644
--- a/apps/Tag/src/com/android/apps/tag/message/TextMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/TextMessage.java
@@ -16,23 +16,28 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.record.ParsedNdefRecord;
import com.android.apps.tag.record.TextRecord;
import com.google.common.base.Preconditions;
+import android.content.Context;
+
+import java.util.List;
import java.util.Locale;
/**
* A message containing one text element
*/
-class TextMessage implements ParsedNdefMessage {
+class TextMessage extends ParsedNdefMessage {
private final TextRecord mRecord;
- TextMessage(TextRecord record) {
+ TextMessage(TextRecord record, List records) {
+ super(Preconditions.checkNotNull(records));
mRecord = Preconditions.checkNotNull(record);
}
@Override
- public String getSnippet(Locale locale) {
+ public String getSnippet(Context context, Locale locale) {
return mRecord.getText();
}
diff --git a/apps/Tag/src/com/android/apps/tag/message/UnknownMessage.java b/apps/Tag/src/com/android/apps/tag/message/UnknownMessage.java
index a30445fc3..fff586f3f 100644
--- a/apps/Tag/src/com/android/apps/tag/message/UnknownMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/UnknownMessage.java
@@ -16,26 +16,27 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.R;
import com.android.apps.tag.record.ParsedNdefRecord;
-import com.google.common.collect.ImmutableList;
+import com.google.common.base.Preconditions;
+import android.content.Context;
+
+import java.util.List;
import java.util.Locale;
/**
* The catchall parsed message format for when nothing else better applies.
*/
-class UnknownMessage implements ParsedNdefMessage {
+class UnknownMessage extends ParsedNdefMessage {
- private final ImmutableList mRecords;
-
- UnknownMessage(Iterable records) {
- mRecords = ImmutableList.copyOf(records);
+ UnknownMessage(List records) {
+ super(Preconditions.checkNotNull(records));
}
@Override
- public String getSnippet(Locale locale) {
- // TODO: localize
- return "Unknown record type with " + mRecords.size() + " elements.";
+ public String getSnippet(Context context, Locale locale) {
+ return context.getString(R.string.tag_unknown);
}
@Override
diff --git a/apps/Tag/src/com/android/apps/tag/message/UriMessage.java b/apps/Tag/src/com/android/apps/tag/message/UriMessage.java
index 85bfa41f1..012306bc6 100644
--- a/apps/Tag/src/com/android/apps/tag/message/UriMessage.java
+++ b/apps/Tag/src/com/android/apps/tag/message/UriMessage.java
@@ -16,26 +16,31 @@
package com.android.apps.tag.message;
+import com.android.apps.tag.record.ParsedNdefRecord;
import com.android.apps.tag.record.UriRecord;
import com.google.common.base.Preconditions;
+import android.content.Context;
+
+import java.util.List;
import java.util.Locale;
/**
* A {@link ParsedNdefMessage} consisting of one {@link UriRecord}.
*/
-class UriMessage implements ParsedNdefMessage {
+class UriMessage extends ParsedNdefMessage {
private final UriRecord mRecord;
- UriMessage(UriRecord record) {
+ UriMessage(UriRecord record, List records) {
+ super(Preconditions.checkNotNull(records));
mRecord = Preconditions.checkNotNull(record);
}
@Override
- public String getSnippet(Locale locale) {
+ public String getSnippet(Context context, Locale locale) {
// URIs cannot be localized
- return mRecord.getUri().toString();
+ return mRecord.getPrettyUriString(context);
}
@Override
diff --git a/apps/Tag/src/com/android/apps/tag/record/ParsedNdefRecord.java b/apps/Tag/src/com/android/apps/tag/record/ParsedNdefRecord.java
index 8de4a3f23..89a6a097c 100644
--- a/apps/Tag/src/com/android/apps/tag/record/ParsedNdefRecord.java
+++ b/apps/Tag/src/com/android/apps/tag/record/ParsedNdefRecord.java
@@ -16,6 +16,11 @@
package com.android.apps.tag.record;
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
/**
* TODO: come up with a better name.
*/
@@ -23,4 +28,9 @@ public interface ParsedNdefRecord {
// Just a placeholder for now. Probably not needed nor desired.
public String getRecordType();
+
+ /**
+ * Returns a view to display this record.
+ */
+ public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent);
}
diff --git a/apps/Tag/src/com/android/apps/tag/record/SmartPoster.java b/apps/Tag/src/com/android/apps/tag/record/SmartPoster.java
index 935b23498..781d51f54 100644
--- a/apps/Tag/src/com/android/apps/tag/record/SmartPoster.java
+++ b/apps/Tag/src/com/android/apps/tag/record/SmartPoster.java
@@ -16,16 +16,25 @@
package com.android.apps.tag.record;
-import android.nfc.FormatException;
-import android.nfc.NdefMessage;
-import android.nfc.NdefRecord;
-
+import com.android.apps.tag.R;
import com.android.apps.tag.message.NdefMessageParser;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
+import android.app.Activity;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
import java.util.Arrays;
import java.util.NoSuchElementException;
+
import javax.annotation.Nullable;
/**
@@ -105,4 +114,23 @@ public class SmartPoster implements ParsedNdefRecord {
public String getRecordType() {
return "SmartPoster";
}
+
+ @Override
+ public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent) {
+ if (mTitleRecord != null) {
+ // Build a container to hold the title and the URI
+ LinearLayout container = new LinearLayout(activity);
+ container.setOrientation(LinearLayout.VERTICAL);
+ container.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+
+ container.addView(mTitleRecord.getView(activity, inflater, container));
+ inflater.inflate(R.layout.tag_divider, container);
+ container.addView(mUriRecord.getView(activity, inflater, container));
+ return container;
+ } else {
+ // Just a URI, return a view for it directly
+ return mUriRecord.getView(activity, inflater, parent);
+ }
+ }
}
diff --git a/apps/Tag/src/com/android/apps/tag/record/TextRecord.java b/apps/Tag/src/com/android/apps/tag/record/TextRecord.java
index 716e5afb7..145a6e8d2 100644
--- a/apps/Tag/src/com/android/apps/tag/record/TextRecord.java
+++ b/apps/Tag/src/com/android/apps/tag/record/TextRecord.java
@@ -16,10 +16,16 @@
package com.android.apps.tag.record;
-import android.nfc.NdefRecord;
-
+import com.android.apps.tag.R;
import com.google.common.base.Preconditions;
+import android.app.Activity;
+import android.nfc.NdefRecord;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
@@ -42,6 +48,13 @@ public class TextRecord implements ParsedNdefRecord {
return "Text";
}
+ @Override
+ public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent) {
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
+ text.setText(mText);
+ return text;
+ }
+
public String getText() {
return mText;
}
diff --git a/apps/Tag/src/com/android/apps/tag/record/UriRecord.java b/apps/Tag/src/com/android/apps/tag/record/UriRecord.java
index 7d5e9bc6f..cebbec9ec 100644
--- a/apps/Tag/src/com/android/apps/tag/record/UriRecord.java
+++ b/apps/Tag/src/com/android/apps/tag/record/UriRecord.java
@@ -16,23 +16,49 @@
package com.android.apps.tag.record;
-import android.net.Uri;
-import android.nfc.NdefRecord;
-
+import com.android.apps.tag.R;
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 android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.nfc.NdefRecord;
+import android.telephony.PhoneNumberUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
import java.nio.charset.Charsets;
import java.util.Arrays;
+import java.util.List;
/**
* A parsed record containing a Uri.
*/
-public class UriRecord implements ParsedNdefRecord {
- private static final byte[] EMPTY = new byte[0];
-
+public class UriRecord implements ParsedNdefRecord, OnClickListener {
+ private static final class ClickInfo {
+ public Activity activity;
+ public Intent intent;
+
+ public ClickInfo(Activity activity, Intent intent) {
+ this.activity = activity;
+ this.intent = intent;
+ }
+ }
+
/**
* NFC Forum "URI Record Type Definition"
*
@@ -89,6 +115,96 @@ public class UriRecord implements ParsedNdefRecord {
return "Uri";
}
+ public Intent getIntentForUri() {
+ String scheme = mUri.getScheme();
+ if ("tel".equals(scheme)) {
+ return new Intent(Intent.ACTION_CALL, mUri);
+ } else if ("sms".equals(scheme) || "smsto".equals(scheme)) {
+ return new Intent(Intent.ACTION_SENDTO, mUri);
+ } else {
+ return new Intent(Intent.ACTION_VIEW, mUri);
+ }
+ }
+
+ public String getPrettyUriString(Context context) {
+ String scheme = mUri.getScheme();
+ boolean tel = "tel".equals(scheme);
+ boolean sms = "sms".equals(scheme) || "smsto".equals(scheme);
+ if (tel || sms) {
+ String ssp = mUri.getSchemeSpecificPart();
+ int offset = ssp.indexOf('?');
+ if (offset >= 0) {
+ ssp = ssp.substring(0, offset);
+ }
+ if (tel) {
+ return context.getString(R.string.action_call, PhoneNumberUtils.formatNumber(ssp));
+ } else {
+ return context.getString(R.string.action_text, PhoneNumberUtils.formatNumber(ssp));
+ }
+ } else {
+ return mUri.toString();
+ }
+ }
+
+ @Override
+ public View getView(Activity activity, LayoutInflater inflater, ViewGroup parent) {
+ Intent intent = getIntentForUri();
+ PackageManager pm = activity.getPackageManager();
+ List activities = pm.queryIntentActivities(intent, 0);
+ int numActivities = activities.size();
+ if (numActivities == 0) {
+ TextView text = (TextView) inflater.inflate(R.layout.tag_text, parent, false);
+ text.setText(mUri.toString());
+ return text;
+ } else if (numActivities == 1) {
+ return buildActivityView(activity, activities.get(0), pm, inflater, parent);
+ } else {
+ // Build a container to hold the multiple entries
+ LinearLayout container = new LinearLayout(activity);
+ container.setOrientation(LinearLayout.VERTICAL);
+ container.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+
+ // Create an entry for each activity that can handle the URI
+ for (ResolveInfo resolveInfo : activities) {
+ if (container.getChildCount() > 0) {
+ inflater.inflate(R.layout.tag_divider, container);
+ }
+ container.addView(buildActivityView(activity, resolveInfo, pm, inflater, container));
+ }
+ return container;
+ }
+ }
+
+ private View buildActivityView(Activity activity, ResolveInfo resolveInfo, PackageManager pm,
+ LayoutInflater inflater, ViewGroup parent) {
+ Intent intent = getIntentForUri();
+ ActivityInfo activityInfo = resolveInfo.activityInfo;
+ intent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
+
+ View item = inflater.inflate(R.layout.tag_uri, parent, false);
+ item.setOnClickListener(this);
+ item.setTag(new ClickInfo(activity, intent));
+
+ ImageView icon = (ImageView) item.findViewById(R.id.icon);
+ icon.setImageDrawable(resolveInfo.loadIcon(pm));
+
+ TextView text = (TextView) item.findViewById(R.id.secondary);
+ text.setText(resolveInfo.loadLabel(pm));
+
+ text = (TextView) item.findViewById(R.id.primary);
+ text.setText(getPrettyUriString(activity));
+
+ return item;
+ }
+
+ @Override
+ public void onClick(View view) {
+ ClickInfo info = (ClickInfo) view.getTag();
+ info.activity.startActivity(info.intent);
+ info.activity.finish();
+ }
+
public Uri getUri() {
return mUri;
}