Hack in tag writing support for non-user builds.

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
diff --git a/src/com/android/apps/tag/MyTagList.java b/src/com/android/apps/tag/MyTagList.java
index 94a35e0..7f330cd 100644
--- a/src/com/android/apps/tag/MyTagList.java
+++ b/src/com/android/apps/tag/MyTagList.java
@@ -27,14 +27,20 @@
 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 @@
 
     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 @@
     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 @@
         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 @@
     }
 
     @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);
diff --git a/src/com/android/apps/tag/TagViewer.java b/src/com/android/apps/tag/TagViewer.java
index 99efc3b..af44f4b 100644
--- a/src/com/android/apps/tag/TagViewer.java
+++ b/src/com/android/apps/tag/TagViewer.java
@@ -18,15 +18,18 @@
 
 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.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 @@
                 }
             }
 
+            // 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 @@
         }
     }
 
+    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;