Merge "Don't crash when not selecting a contact. Fix some more UI stuff. Add text type." into gingerbread
diff --git a/res/layout/tag_edit_text.xml b/res/layout/tag_edit_text.xml
new file mode 100644
index 0000000..c316e16
--- /dev/null
+++ b/res/layout/tag_edit_text.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:padding="8dip"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/title"
+        android:text="@string/tag_text"
+        style="@style/record_title"
+        android:layout_alignParentTop="true"
+        />
+
+    <EditText
+        android:id="@+id/value"
+        android:inputType="textShortMessage|textCapSentences"
+        android:layout_below="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+</RelativeLayout>
+
+
diff --git a/src/com/android/apps/tag/ContentSelectorAdapter.java b/src/com/android/apps/tag/ContentSelectorAdapter.java
new file mode 100644
index 0000000..ac78a7c
--- /dev/null
+++ b/src/com/android/apps/tag/ContentSelectorAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * 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.record.TextRecord;
+import com.android.apps.tag.record.UriRecord;
+import com.android.apps.tag.record.VCardRecord;
+
+import android.content.Context;
+import android.nfc.NdefRecord;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+
+/**
+ * An {@link Adapter} that presents options to select data which can be written into a
+ * {@link NdefRecord} for an NFC tag.
+ */
+public class ContentSelectorAdapter extends BaseAdapter {
+    private Context mContext;
+    private final LayoutInflater mInflater;
+    private String[] mSupportedTypes;
+    private View[] mViews;
+
+    public ContentSelectorAdapter(Context context, String[] supportedTypes) {
+        mContext = context;
+        mInflater = LayoutInflater.from(context);
+        mSupportedTypes = supportedTypes;
+        mViews = new View[supportedTypes.length];
+    }
+
+    public void setListView(ListView list) {
+        int size = mSupportedTypes.length;
+        for (int i = 0; i < size; i++) {
+            String type = mSupportedTypes[i];
+            if (UriRecord.RECORD_TYPE.equals(type)) {
+                mViews[i] = UriRecord.getAddView(mContext, mInflater, list);
+            } else if (VCardRecord.RECORD_TYPE.equals(type)) {
+                mViews[i] = VCardRecord.getAddView(mContext, mInflater, list);
+            } else if (TextRecord.RECORD_TYPE.equals(type)) {
+                mViews[i] = TextRecord.getAddView(mContext, mInflater, list);
+            } else {
+                throw new IllegalArgumentException("Not a supported view type");
+            }
+        }
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return IGNORE_ITEM_VIEW_TYPE;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public long getItemId(int pos) {
+        return pos;
+    }
+
+    @Override
+    public int getCount() {
+        return mSupportedTypes.length;
+    }
+
+    @Override
+    public View getView(int position, View recycle, ViewGroup parent) {
+        return mViews[position];
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return mViews[position].getTag();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/apps/tag/EditTagActivity.java b/src/com/android/apps/tag/EditTagActivity.java
index edaff80..e04421c 100644
--- a/src/com/android/apps/tag/EditTagActivity.java
+++ b/src/com/android/apps/tag/EditTagActivity.java
@@ -19,11 +19,13 @@
 import com.android.apps.tag.message.NdefMessageParser;
 import com.android.apps.tag.message.ParsedNdefMessage;
 import com.android.apps.tag.provider.TagContract.NdefMessages;
-import com.android.apps.tag.record.ImageRecord;
 import com.android.apps.tag.record.ParsedNdefRecord;
 import com.android.apps.tag.record.RecordEditInfo;
 import com.android.apps.tag.record.RecordEditInfo.EditCallbacks;
+import com.android.apps.tag.record.TextRecord;
+import com.android.apps.tag.record.TextRecord.TextRecordEditInfo;
 import com.android.apps.tag.record.UriRecord;
+import com.android.apps.tag.record.UriRecord.UriRecordEditInfo;
 import com.android.apps.tag.record.VCardRecord;
 import com.google.common.collect.ImmutableSet;
 
@@ -66,7 +68,8 @@
 
     protected static final Set<String> SUPPORTED_RECORD_TYPES = ImmutableSet.of(
         VCardRecord.RECORD_TYPE,
-        UriRecord.RECORD_TYPE
+        UriRecord.RECORD_TYPE,
+        TextRecord.RECORD_TYPE
     );
 
     /**
@@ -134,7 +137,6 @@
         root.addView(editView);
     }
 
-
     @Override
     public void startPickForRecord(RecordEditInfo editInfo, Intent intent) {
         startActivityForResult(intent, 0);
@@ -147,6 +149,8 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if ((resultCode != RESULT_OK) || (data == null)) {
+            // No valid data, close the editor.
+            finish();
             return;
         }
 
@@ -154,9 +158,15 @@
         try {
             mRecord.handlePickResult(this, data);
         } catch (IllegalArgumentException ex) {
-            // TODO: handle.
+            // No valid data, close the editor.
+            finish();
             return;
         }
+
+        // Update the title to indicate that we're adding a tag instead of editing.
+        setTitle(R.string.add_tag);
+
+        // Setup the tag views
         refresh();
     }
 
@@ -196,12 +206,7 @@
             try {
                 if (cursor != null && cursor.moveToFirst()) {
                     msg = new NdefMessage(cursor.getBlob(GetTagQuery.COLUMN_BYTES));
-                    if (msg != null) {
-                        populateFromMessage(msg);
-                    } else {
-                        // TODO: do something more graceful.
-                        finish();
-                    }
+                    populateFromMessage(msg);
                 }
             } catch (FormatException e) {
                 Log.e(LOG_TAG, "Unable to parse tag for editing.", e);
@@ -248,7 +253,7 @@
         }
     }
 
-    private void populateFromMessage(NdefMessage refMessage) {
+    void populateFromMessage(NdefMessage refMessage) {
         // Locally stored message.
         ParsedNdefMessage parsed = NdefMessageParser.parse(refMessage);
         List<ParsedNdefRecord> records = parsed.getRecords();
@@ -277,12 +282,15 @@
                 URL parsed = new URL(text);
 
                 // Valid URL.
-                mRecord = new UriRecord.UriRecordEditInfo(text);
+                mRecord = new UriRecordEditInfo(text);
                 refresh();
                 return true;
 
             } catch (MalformedURLException ex) {
-                // TODO: handle.
+                // Random text
+                mRecord = new TextRecordEditInfo(text);
+                refresh();
+                return true;
             }
 
         } else if ("text/x-vcard".equals(type)) {
@@ -304,6 +312,12 @@
      * Saves the content of the tag.
      */
     private void saveAndFinish() {
+        if (mRecord == null) {
+            setResult(RESULT_CANCELED);
+            finish();
+            return;
+        }
+
         NdefMessage msg = new NdefMessage(new NdefRecord[] { mRecord.getValue() });
 
         if (Intent.ACTION_SEND.equals(getIntent().getAction())) {
diff --git a/src/com/android/apps/tag/MyTagList.java b/src/com/android/apps/tag/MyTagList.java
index 3ba03c8..e4541c5 100644
--- a/src/com/android/apps/tag/MyTagList.java
+++ b/src/com/android/apps/tag/MyTagList.java
@@ -16,9 +16,9 @@
 
 package com.android.apps.tag;
 
-import com.android.apps.tag.TagContentSelector.SelectContentCallbacks;
 import com.android.apps.tag.provider.TagContract.NdefMessages;
 import com.android.apps.tag.record.RecordEditInfo;
+import com.android.apps.tag.record.TextRecord;
 import com.android.apps.tag.record.UriRecord;
 import com.android.apps.tag.record.VCardRecord;
 import com.google.common.base.Preconditions;
@@ -74,7 +74,8 @@
 public class MyTagList
         extends Activity
         implements OnItemClickListener, View.OnClickListener,
-                   SelectContentCallbacks, TagService.SaveCallbacks {
+                   TagService.SaveCallbacks,
+                   DialogInterface.OnClickListener {
 
     static final String TAG = "TagList";
 
@@ -86,6 +87,11 @@
     private static final String PREF_KEY_ACTIVE_TAG = "active-my-tag";
     static final String PREF_KEY_TAG_TO_WRITE = "tag-to-write";
 
+    static final String[] SUPPORTED_TYPES = new String[] {
+            VCardRecord.RECORD_TYPE,
+            UriRecord.RECORD_TYPE,
+            TextRecord.RECORD_TYPE,
+    };
 
     private View mSelectActiveTagAnchor;
     private View mActiveTagDetails;
@@ -362,21 +368,6 @@
     }
 
     @Override
-    public Set<String> getSupportedTypes() {
-        return ImmutableSet.of(
-                VCardRecord.RECORD_TYPE,
-                UriRecord.RECORD_TYPE
-        );
-    }
-
-    @Override
-    public void onSelectContent(RecordEditInfo info) {
-        Intent intent = new Intent(this, EditTagActivity.class);
-        intent.putExtra(EditTagActivity.EXTRA_NEW_RECORD_INFO, info);
-        startActivityForResult(intent, REQUEST_EDIT);
-    }
-
-    @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
         Cursor cursor = mAdapter.getCursor();
         if (cursor == null
@@ -472,19 +463,42 @@
 
     @Override
     protected Dialog onCreateDialog(int id, Bundle args) {
+        Context lightTheme = new ContextThemeWrapper(this, android.R.style.Theme_Light);
         if (id == DIALOG_ID_SELECT_ACTIVE_TAG) {
-            Context lightTheme = new ContextThemeWrapper(this, android.R.style.Theme_Light);
             SelectActiveTagDialog dialog = new SelectActiveTagDialog(lightTheme,
                     mAdapter.getCursor());
             dialog.setInverseBackgroundForced(true);
             mSelectActiveTagDialog = new WeakReference<SelectActiveTagDialog>(dialog);
             return dialog;
         } else if (id == DIALOG_ID_ADD_NEW_TAG) {
-            return new TagContentSelector(this, this);
+            ContentSelectorAdapter adapter = new ContentSelectorAdapter(lightTheme,
+                    SUPPORTED_TYPES);
+            AlertDialog dialog = new AlertDialog.Builder(lightTheme)
+                    .setTitle(R.string.select_type)
+                    .setIcon(0)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .setAdapter(adapter, this)
+                    .create();
+            adapter.setListView(dialog.getListView());
+            dialog.setInverseBackgroundForced(true);
+            return dialog;
         }
         return super.onCreateDialog(id, args);
     }
 
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_NEGATIVE) {
+            dialog.cancel();
+        } else {
+            RecordEditInfo info = (RecordEditInfo) ((AlertDialog) dialog).getListView()
+                    .getAdapter().getItem(which);
+            Intent intent = new Intent(this, EditTagActivity.class);
+            intent.putExtra(EditTagActivity.EXTRA_NEW_RECORD_INFO, info);
+            startActivityForResult(intent, REQUEST_EDIT);
+        }
+    }
+
     /**
      * Selects the tag to be used as the "My tag" shared tag.
      *
diff --git a/src/com/android/apps/tag/TagContentSelector.java b/src/com/android/apps/tag/TagContentSelector.java
deleted file mode 100644
index 3de77fa..0000000
--- a/src/com/android/apps/tag/TagContentSelector.java
+++ /dev/null
@@ -1,117 +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 com.android.apps.tag.record.RecordEditInfo;
-import com.android.apps.tag.record.UriRecord;
-import com.android.apps.tag.record.VCardRecord;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.nfc.NdefRecord;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.Set;
-
-/**
- * A {@link Dialog} that presents options to select data which can be written into a
- * {@link NdefRecord} for an NFC tag.
- */
-public class TagContentSelector extends AlertDialog
-        implements DialogInterface.OnClickListener, android.view.View.OnClickListener {
-
-    private final ViewGroup mListRoot;
-    private final LayoutInflater mInflater;
-    private final SelectContentCallbacks mCallbacks;
-
-    public interface SelectContentCallbacks {
-        /**
-         * Determines which data types should be displayed in this selector.
-         * Keys correspond to types in {@link RecordEditInfo}.
-         */
-        Set<String> getSupportedTypes();
-
-        /**
-         * Handle a selection of new data for an {@link NdefRecord}.
-         */
-        void onSelectContent(RecordEditInfo info);
-    }
-
-    public TagContentSelector(Activity activity, SelectContentCallbacks callbacks) {
-        super(activity);
-        mCallbacks = callbacks;
-
-        setTitle(activity.getResources().getString(R.string.select_type));
-
-        mInflater = LayoutInflater.from(activity);
-        ViewGroup root = (ViewGroup) mInflater.inflate(R.layout.tag_content_selector, null);
-        mListRoot = (ViewGroup) root.findViewById(R.id.list);
-
-        rebuildViews();
-
-        setView(root);
-        setIcon(0);
-        setButton(
-                DialogInterface.BUTTON_POSITIVE,
-                activity.getString(android.R.string.cancel),
-                this);
-    }
-
-    /**
-     * Builds a {@link View} used as an item in a list when picking a new piece of content to add
-     * to the tag.
-     */
-    public View getAddView(ViewGroup parent, String type) {
-        if (UriRecord.RECORD_TYPE.equals(type)) {
-            return UriRecord.getAddView(getContext(), mInflater, parent);
-        } else if (VCardRecord.RECORD_TYPE.equals(type)) {
-            return VCardRecord.getAddView(getContext(), mInflater, parent);
-        }
-        throw new IllegalArgumentException("Not a supported view type");
-    }
-
-
-    public void rebuildViews() {
-        mListRoot.removeAllViews();
-        for (String type : mCallbacks.getSupportedTypes()) {
-            View selectItemView = getAddView(mListRoot, type);
-            if (selectItemView != null) {
-                selectItemView.setOnClickListener(this);
-                mListRoot.addView(selectItemView);
-            }
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        dismiss();
-    }
-
-    @Override
-    public void onClick(View target) {
-        Object tag = target.getTag();
-        if ((tag == null) || !(tag instanceof RecordEditInfo)) {
-            return;
-        }
-        mCallbacks.onSelectContent((RecordEditInfo) tag);
-        dismiss();
-    }
-}
diff --git a/src/com/android/apps/tag/record/AbstractTextRecordEditInfo.java b/src/com/android/apps/tag/record/AbstractTextRecordEditInfo.java
new file mode 100644
index 0000000..235de72
--- /dev/null
+++ b/src/com/android/apps/tag/record/AbstractTextRecordEditInfo.java
@@ -0,0 +1,97 @@
+/*
+ * 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.record;
+
+import com.android.apps.tag.R;
+import com.google.common.base.Preconditions;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Parcel;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+public abstract class AbstractTextRecordEditInfo extends RecordEditInfo implements TextWatcher {
+    private String mCurrentValue;
+    private EditText mEditText;
+
+    public AbstractTextRecordEditInfo(String initialValue) {
+        super(UriRecord.RECORD_TYPE);
+        mCurrentValue = Preconditions.checkNotNull(initialValue);
+    }
+
+    public AbstractTextRecordEditInfo(Parcel parcel) {
+        super(parcel);
+        mCurrentValue = parcel.readString();
+    }
+
+    public String getCurrentText() {
+        return mCurrentValue;
+    }
+
+    @Override
+    public Intent getPickIntent() {
+        return null;
+    }
+
+    @Override
+    public void handlePickResult(Context context, Intent data) {
+        // Not supported.
+    }
+
+    public abstract int getLayoutId();
+    
+    @Override
+    public View getEditView(
+            Activity activity, LayoutInflater inflater,
+            ViewGroup parent, EditCallbacks callbacks) {
+        View view = buildEditView(activity, inflater, getLayoutId(), parent, callbacks);
+        mEditText = (EditText) view.findViewById(R.id.value);
+        mEditText.setText(mCurrentValue);
+        mEditText.addTextChangedListener(this);
+        return view;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        super.writeToParcel(out, flags);
+        out.writeString(mCurrentValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        mCurrentValue = s.toString();
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/apps/tag/record/TextRecord.java b/src/com/android/apps/tag/record/TextRecord.java
index e1945ae..edfeab4 100644
--- a/src/com/android/apps/tag/record/TextRecord.java
+++ b/src/com/android/apps/tag/record/TextRecord.java
@@ -17,21 +17,31 @@
 package com.android.apps.tag.record;
 
 import com.android.apps.tag.R;
+import com.android.apps.tag.record.UriRecord.UriRecordEditInfo;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.primitives.Bytes;
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.nfc.NdefRecord;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -39,6 +49,8 @@
  */
 public class TextRecord extends ParsedNdefRecord {
 
+    public static final String RECORD_TYPE = "TextRecord";
+
     /** ISO/IANA language code */
     private final String mLanguageCode;
     private final String mText;
@@ -74,6 +86,24 @@
         return mLanguageCode;
     }
 
+    @Override
+    public RecordEditInfo getEditInfo(Activity host) {
+        return new TextRecordEditInfo(mText);
+    }
+
+    /**
+     * Returns a view in a list of record types for adding new records to a message.
+     */
+    public static View getAddView(Context context, LayoutInflater inflater, ViewGroup parent) {
+        ViewGroup root = (ViewGroup) inflater.inflate(
+                R.layout.tag_add_record_list_item, parent, false);
+        ((ImageView) root.findViewById(R.id.image)).setImageResource(R.drawable.ic_launcher_nfc);
+        ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.tag_text));
+
+        root.setTag(new TextRecordEditInfo(""));
+        return root;
+    }
+
     // TODO: deal with text fields which span multiple NdefRecords
     public static TextRecord parse(NdefRecord record) {
         Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
@@ -146,4 +176,39 @@
 
         return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
     }
+
+    public static class TextRecordEditInfo extends AbstractTextRecordEditInfo {
+        public TextRecordEditInfo(String text) {
+            super(text);
+        }
+
+        public TextRecordEditInfo(Parcel in) {
+            super(in);
+        }
+
+        @Override
+        public int getLayoutId() {
+            return R.layout.tag_edit_text;
+        }
+
+        @Override
+        public NdefRecord getValue() {
+            String text = getCurrentText();
+            if (TextUtils.isEmpty(text)) text = "";
+            return TextRecord.newTextRecord(text, Locale.getDefault(), true);
+        }
+
+        public static final Parcelable.Creator<TextRecordEditInfo> CREATOR =
+                new Parcelable.Creator<TextRecordEditInfo>() {
+            @Override
+            public TextRecordEditInfo createFromParcel(Parcel in) {
+                return new TextRecordEditInfo(in);
+            }
+
+            @Override
+            public TextRecordEditInfo[] newArray(int size) {
+                return new TextRecordEditInfo[size];
+            }
+        };
+    }
 }
diff --git a/src/com/android/apps/tag/record/UriRecord.java b/src/com/android/apps/tag/record/UriRecord.java
index e598937..f253c82 100644
--- a/src/com/android/apps/tag/record/UriRecord.java
+++ b/src/com/android/apps/tag/record/UriRecord.java
@@ -17,8 +17,6 @@
 package com.android.apps.tag.record;
 
 import com.android.apps.tag.R;
-import com.android.apps.tag.record.RecordEditInfo.EditCallbacks;
-import com.android.apps.tag.record.UriRecord.UriRecordEditInfo;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.BiMap;
@@ -36,14 +34,12 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.PhoneNumberUtils;
-import android.text.Editable;
-import android.text.TextWatcher;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -189,7 +185,7 @@
         ((ImageView) root.findViewById(R.id.image)).setImageDrawable(info.loadIcon(pm));
         ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.url));
 
-        root.setTag(new UriRecordEditInfo());
+        root.setTag(new UriRecordEditInfo(""));
         return root;
     }
 
@@ -278,84 +274,39 @@
                 NdefRecord.RTD_URI, EMPTY, payload);
     }
 
-    public static class UriRecordEditInfo extends RecordEditInfo implements TextWatcher {
-        private String mCurrentValue;
-        private EditText mEditText;
-
-        public UriRecordEditInfo(String initialValue) {
-            super(RECORD_TYPE);
-            mCurrentValue = Preconditions.checkNotNull(initialValue);
+    public static class UriRecordEditInfo extends AbstractTextRecordEditInfo {
+        public UriRecordEditInfo(String uri) {
+            super(uri);
         }
 
-        public UriRecordEditInfo() {
-            this("");
-        }
-
-        protected UriRecordEditInfo(Parcel parcel) {
-            super(parcel);
-            mCurrentValue = parcel.readString();
+        public UriRecordEditInfo(Parcel in) {
+            super(in);
         }
 
         @Override
-        public Intent getPickIntent() {
-            return null;
-        }
-
-        @Override
-        public void handlePickResult(Context context, Intent data) {
-            // Not supported.
-        }
-
-        @Override
-        public View getEditView(
-                Activity activity, LayoutInflater inflater,
-                ViewGroup parent, EditCallbacks callbacks) {
-            View view = buildEditView(activity, inflater, R.layout.tag_edit_url, parent, callbacks);
-            mEditText = (EditText) view.findViewById(R.id.value);
-            mEditText.setText(mCurrentValue);
-            mEditText.addTextChangedListener(this);
-            return view;
+        public int getLayoutId() {
+            return R.layout.tag_edit_url;
         }
 
         @Override
         public NdefRecord getValue() {
-            return UriRecord.newUriRecord(Uri.parse(mCurrentValue));
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeString(mCurrentValue);
+            String text = getCurrentText();
+            if (TextUtils.isEmpty(text)) text = "";
+            return UriRecord.newUriRecord(Uri.parse(text));
         }
 
         @SuppressWarnings("unused")
         public static final Parcelable.Creator<UriRecordEditInfo> CREATOR =
                 new Parcelable.Creator<UriRecordEditInfo>() {
+            @Override
             public UriRecordEditInfo createFromParcel(Parcel in) {
                 return new UriRecordEditInfo(in);
             }
 
+            @Override
             public UriRecordEditInfo[] newArray(int size) {
                 return new UriRecordEditInfo[size];
             }
         };
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void afterTextChanged(Editable s) {
-            mCurrentValue = s.toString();
-        }
-
-        @Override
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        }
-
-        @Override
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-        }
     }
 }