Hack in tag writing support for non-user builds.
Jeff Hamilton [Fri, 17 Dec 2010 22:16:21 +0000 (16:16 -0600)]
The UX is horrible, but it does let you do
basic tag writing. To use it go to My Tag
create a tag you want to write the select
"Write to next tag scanned" from the context
menu of the tag you want to write.

The next time any tag is scanned it will try
to write that message and will show a toast
with the result of the write attempt.

Regardless of success the write request will
be erased at the scan.

Change-Id: Ib4eb4762979bf178bfdb588b7d8a6788b02631d2

src/com/android/apps/tag/MyTagList.java
src/com/android/apps/tag/TagViewer.java

index 94a35e0..7f330cd 100644 (file)
@@ -27,14 +27,20 @@ import android.content.ContentUris;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.database.CharArrayBuffer;
 import android.database.Cursor;
 import android.nfc.FormatException;
 import android.nfc.NdefMessage;
 import android.nfc.NfcAdapter;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -63,6 +69,7 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
 
     private static final String BUNDLE_KEY_TAG_ID_IN_EDIT = "tag-edit";
     private static final String PREF_KEY_ACTIVE_TAG = "active-my-tag";
+    static final String PREF_KEY_TAG_TO_WRITE = "tag-to-write";
 
     private View mSelectActiveTagAnchor;
     private View mActiveTagDetails;
@@ -76,6 +83,8 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
     private WeakReference<SelectActiveTagDialog> mSelectActiveTagDialog;
     private long mTagIdInEdit = -1;
 
+    private boolean mWriteSupport = false;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -104,11 +113,17 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
         mList = (ListView) findViewById(android.R.id.list);
         mList.setAdapter(mAdapter);
         mList.setOnItemClickListener(this);
-        registerForContextMenu(mList);
         findViewById(R.id.add_tag).setOnClickListener(this);
 
         // Kick off an async task to load the tags.
         new TagLoaderTask().execute((Void[]) null);
+
+        // If we're not on a user build offer a back door for writing tags.
+        // The UX is horrible so we don't want to ship it but need it for testing.
+        if (!Build.TYPE.equalsIgnoreCase("user")) {
+            mWriteSupport = true;
+            registerForContextMenu(mList);
+        }
     }
 
     @Override
@@ -132,6 +147,28 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
     }
 
     @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        if (mWriteSupport) {
+            menu.add(0, 1, 0, "Write to next tag scanned");
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        AdapterView.AdapterContextMenuInfo info;
+        try {
+             info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+        } catch (ClassCastException e) {
+            Log.e(TAG, "bad menuInfo", e);
+            return false;
+        }
+
+        SharedPreferences prefs = getSharedPreferences("tags.pref", Context.MODE_PRIVATE);
+        prefs.edit().putLong(PREF_KEY_TAG_TO_WRITE, info.id).apply();
+        return true;
+    }
+
+    @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         // TODO: use implicit Intent?
         Intent intent = new Intent(this, EditTagActivity.class);
index 99efc3b..af44f4b 100644 (file)
@@ -18,15 +18,18 @@ package com.android.apps.tag;
 
 import com.android.apps.tag.message.NdefMessageParser;
 import com.android.apps.tag.message.ParsedNdefMessage;
+import com.android.apps.tag.provider.TagContract;
 import com.android.apps.tag.provider.TagContract.NdefMessages;
 import com.android.apps.tag.record.ParsedNdefRecord;
 
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
@@ -35,6 +38,10 @@ import android.nfc.FormatException;
 import android.nfc.NdefMessage;
 import android.nfc.NdefRecord;
 import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.nfc.technology.Ndef;
+import android.nfc.technology.NdefFormatable;
+import android.nfc.technology.TagTechnology;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -176,6 +183,18 @@ public class TagViewer extends Activity implements OnClickListener {
                 }
             }
 
+            // Check to see if there's a tag queued up for writing.
+            SharedPreferences prefs = getSharedPreferences("tags.pref", Context.MODE_PRIVATE);
+            long tagToWrite = prefs.getLong(MyTagList.PREF_KEY_TAG_TO_WRITE, 0);
+            prefs.edit().putLong(MyTagList.PREF_KEY_TAG_TO_WRITE, 0).apply();
+            if (tagToWrite != 0) {
+                if (writeTag((Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG), tagToWrite)) {
+                    Toast.makeText(this, "Tag written", Toast.LENGTH_SHORT).show();
+                    finish();
+                    return;
+                }
+            }
+
             // When a tag is discovered we send it to the service to be save. We
             // include a PendingIntent for the service to call back onto. This
             // will cause this activity to be restarted with onNewIntent(). At
@@ -234,6 +253,53 @@ public class TagViewer extends Activity implements OnClickListener {
         }
     }
 
+    private boolean writeTag(Tag tag, long id) {
+        try {
+            NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
+            Cursor cursor = getContentResolver().query(
+                    ContentUris.withAppendedId(NdefMessages.CONTENT_URI, id),
+                    new String[] { NdefMessages.BYTES }, null, null, null);
+            if (cursor == null || !cursor.moveToFirst()) {
+                return false;
+            }
+
+            byte[] bytes = cursor.getBlob(0);
+            cursor.close();
+            NdefMessage msg = new NdefMessage(bytes);
+
+            Ndef ndef = (Ndef) tag.getTechnology(adapter, TagTechnology.NDEF);
+            if (ndef != null) {
+                ndef.connect();
+                if (!ndef.isWritable()) {
+                    Toast.makeText(this, "Tag is read-only, not writing", Toast.LENGTH_SHORT)
+                            .show();
+                    return false;
+                }
+                ndef.writeNdefMessage(msg);
+                Toast.makeText(this, "Wrote message to pre-formatted tag", Toast.LENGTH_SHORT)
+                        .show();
+                return true;
+            } else {
+                NdefFormatable format = (NdefFormatable) tag.getTechnology(adapter,
+                        TagTechnology.NDEF_FORMATABLE);
+                if (format != null) {
+                    format.connect();
+                    format.format(msg);
+                    Toast.makeText(this, "Formatted tag and wrote message", Toast.LENGTH_SHORT)
+                            .show();
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to write tag", e);
+        }
+
+        Toast.makeText(this, "Failed to write tag", Toast.LENGTH_SHORT)
+                .show();
+
+        return false;
+    }
+
     void buildTagViews(NdefMessage[] msgs) {
         if (msgs == null || msgs.length == 0) {
             return;