Large simplification to My tags functionality.
Ben Komalo [Fri, 14 Jan 2011 02:14:21 +0000 (18:14 -0800)]
- removed image record editing
- removed mandatory text in all My tags
- made the My tags fixed number of records
  the ability to add/remove records
- also fixes unparcelling bugs

Change-Id: Iab6da6eb309ee8ba43cabaa45be1c9442c229a47

14 files changed:
AndroidManifest.xml
res/layout/edit_tag_activity.xml
res/layout/tag_edit_image.xml
res/layout/tag_edit_url.xml
res/layout/tag_edit_vcard.xml
res/values/strings.xml
src/com/android/apps/tag/EditTagActivity.java
src/com/android/apps/tag/MyTagList.java
src/com/android/apps/tag/TagContentSelector.java
src/com/android/apps/tag/provider/TagDBHelper.java
src/com/android/apps/tag/record/ImageRecord.java
src/com/android/apps/tag/record/RecordEditInfo.java
src/com/android/apps/tag/record/UriRecord.java
src/com/android/apps/tag/record/VCardRecord.java

index 3a58005..afeded9 100644 (file)
@@ -69,6 +69,7 @@
             </intent-filter>
         </activity>
 
+        <!-- Make the activity show up as "My tag" when resolving intent -->
         <activity android:name="EditTagActivity" android:label="@string/tab_my_tag">
             <intent-filter>
                 <action android:name="android.intent.action.SEND"/>
index 151cd48..6781961 100644 (file)
@@ -23,66 +23,15 @@ limitations under the License.
 >
 
     <ScrollView
-        android:layout_weight="1"
+        android:id="@+id/content_parent"
+        android:layout_weight="1.0"
         android:layout_width="match_parent"
-        android:layout_height="0dip">
-
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <!-- Tag text -->
-            <LinearLayout
-                android:padding="8dip"
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" >
-
-                <TextView
-                    android:text="@string/tag_text"
-                    style="@style/record_title"
-                    />
-
-                <EditText
-                    android:id="@+id/input_tag_text"
-                    android:inputType="textMultiLine"
-                    android:lines="3"
-                    android:gravity="top"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content" />
-
-            </LinearLayout>
-
-            <LinearLayout
-                android:id="@+id/content_parent"
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-
-            <include layout="@layout/tag_divider" />
-
-            <!-- Control to add content. -->
-            <TextView
-                android:id="@+id/add_content_target"
-                android:text="@string/add_content"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:paddingLeft="8dip"
-                android:paddingRight="8dip"
-                android:paddingTop="16dip"
-                android:paddingBottom="16dip"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-
-            <include layout="@layout/tag_divider" />
-
-        </LinearLayout>
-    </ScrollView>
+        android:layout_height="0dip" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
+        android:layout_alignParentBottom="true"
     >
         <Button
             android:id="@+id/save"
@@ -99,4 +48,5 @@ limitations under the License.
             android:layout_height="wrap_content" />
 
     </LinearLayout>
+
 </LinearLayout>
index bdd07cf..b924fc0 100644 (file)
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-    <ImageView
-        android:src="@drawable/ic_btn_round_minus"
-        android:id="@+id/delete"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentRight="true"
-    />
-
     <TextView
         android:id="@+id/title"
         android:text="@string/photo"
         style="@style/record_title"
         android:layout_alignParentTop="true"
-        android:layout_toLeftOf="@+id/delete"
     />
 
     <ImageView
index 86758e2..568735b 100644 (file)
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-    <ImageView
-        android:src="@drawable/ic_btn_round_minus"
-        android:id="@+id/delete"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentRight="true"
-    />
-
     <TextView
         android:id="@+id/title"
         android:text="@string/url"
         style="@style/record_title"
         android:layout_alignParentTop="true"
-        android:layout_toLeftOf="@+id/delete"
         />
 
     <EditText
index d2b6b9a..90e8d86 100644 (file)
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-    <ImageView
-        android:src="@drawable/ic_btn_round_minus"
-        android:id="@+id/delete"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentRight="true"
-    />
-
     <TextView
         android:id="@+id/title"
         android:text="@string/contact"
         style="@style/record_title"
         android:layout_alignParentTop="true"
-        android:layout_toLeftOf="@+id/delete"
     />
 
     <LinearLayout
index 47037d1..47fd1e8 100644 (file)
@@ -64,6 +64,9 @@
     <!-- Label for control which enables/disables the "my tag" feature. -->
     <string name="turn_on_my_tag">Share my tag</string>
 
+    <!-- Label for activity which edits a tag to share/write [CHAR LIMIT=60] -->
+    <string name="edit_tag">Edit tag</string>
+
     <!-- Description for the control which enables/disables the "my tag" feature. -->
     <string name="turn_on_my_tag_subtitle">Allow others to read my tag </string>
 
index 5c6faa1..3694db3 100644 (file)
@@ -23,15 +23,11 @@ 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.UriRecord;
 import com.android.apps.tag.record.VCardRecord;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
 
 import android.app.Activity;
-import android.app.Dialog;
 import android.content.Intent;
 import android.database.Cursor;
 import android.net.Uri;
@@ -47,40 +43,36 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.EditText;
 
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Locale;
 import java.util.Set;
 
 /**
  * A base {@link Activity} class for an editor of an {@link NdefMessage} tag.
  *
- * The core of the editing is done by various child {@link View}s that differ based on
- * {@link ParsedNdefRecord} types. Each type of {@link ParsedNdefRecord} can build views to
+ * The core of the editing is done by various a {@link View}s that differs based on
+ * {@link ParsedNdefRecord} types. Each type of {@link ParsedNdefRecord} can build a to
  * pick/select a new piece of content, or edit an existing content for the {@link NdefMessage}.
  */
 public class EditTagActivity extends Activity implements OnClickListener, EditCallbacks {
 
     private static final String LOG_TAG = "Tags";
 
-    private static final String BUNDLE_KEY_OUTSTANDING_PICK = "outstanding-pick";
-    protected static final int DIALOG_ID_ADD_CONTENT = 0;
-    public static final String EXTRA_RESULT_MSG = "msg";
+    protected static final String BUNDLE_KEY_OUTSTANDING_PICK = "outstanding-pick";
+    public static final String EXTRA_RESULT_MSG = "com.android.apps.tag.msg";
+    public static final String EXTRA_NEW_RECORD_INFO = "com.android.apps.tag.new_record";
 
-    private static final Set<String> SUPPORTED_RECORD_TYPES = ImmutableSet.of(
-        ImageRecord.RECORD_TYPE,
-        UriRecord.RECORD_TYPE,
-        VCardRecord.RECORD_TYPE
+    protected static final Set<String> SUPPORTED_RECORD_TYPES = ImmutableSet.of(
+        VCardRecord.RECORD_TYPE,
+        UriRecord.RECORD_TYPE
     );
 
     /**
-     * Records contained in the current message being edited.
+     * The underlying record in the tag being edited.
      */
-    protected final ArrayList<RecordEditInfo> mRecords = Lists.newArrayList();
+    private RecordEditInfo mRecord;
 
     /**
      * The container where the subviews for each record are housed.
@@ -88,35 +80,24 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
     private ViewGroup mContentRoot;
 
     /**
-     * Info about an outstanding picking activity to add a new record.
-     */
-    private RecordEditInfo mRecordWithOutstandingPick;
-
-    /**
      * Whether or not data was already parsed from an {@link Intent}. This happens when the user
      * shares data via the My tag feature.
      */
     private boolean mParsedIntent = false;
 
-    private EditText mTextView;
-
     private LayoutInflater mInflater;
 
     @Override
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
+
         setContentView(R.layout.edit_tag_activity);
+        setTitle(getResources().getString(R.string.edit_tag));
 
-        if (savedState != null) {
-            mRecordWithOutstandingPick = savedState.getParcelable(BUNDLE_KEY_OUTSTANDING_PICK);
-        }
         mInflater = LayoutInflater.from(this);
-
-        findViewById(R.id.add_content_target).setOnClickListener(this);
         findViewById(R.id.save).setOnClickListener(this);
         findViewById(R.id.cancel).setOnClickListener(this);
 
-        mTextView = (EditText) findViewById(R.id.input_tag_text);
         mContentRoot = (ViewGroup) findViewById(R.id.content_parent);
 
         resolveIntent();
@@ -131,152 +112,53 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
     }
 
     /**
-     * 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 (ImageRecord.RECORD_TYPE.equals(type)) {
-            return ImageRecord.getAddView(this, mInflater, parent);
-        } else if (UriRecord.RECORD_TYPE.equals(type)) {
-            return UriRecord.getAddView(this, mInflater, parent);
-        } else if (VCardRecord.RECORD_TYPE.equals(type)) {
-            return VCardRecord.getAddView(this, mInflater, parent);
-        }
-        throw new IllegalArgumentException("Not a supported view type");
-    }
-
-    /**
-     * Builds a snapshot of current values as held in the internal state of this editor.
+     * Builds a snapshot of current value as held in the internal state of this editor.
      */
-    public ArrayList<NdefRecord> getValues() {
-        ArrayList<NdefRecord> result = new ArrayList<NdefRecord>(mRecords.size());
-        for (RecordEditInfo editInfo : mRecords) {
-            result.add(editInfo.getValue());
-        }
-        return result;
+    public NdefRecord getValue() {
+        return mRecord.getValue();
     }
 
     /**
-     * Builds a {@link View} used as an item in a list when editing content for a tag.
+     * Refreshes the UI with updated content from the record.
+     * Typically used when the records require an external {@link Activity} to edit.
      */
-    public void addRecord(RecordEditInfo editInfo) {
-        mRecords.add(Preconditions.checkNotNull(editInfo));
-        addViewForRecord(editInfo);
-    }
-
-    /**
-     * Adds a child editor view for a record.
-     */
-    public void addViewForRecord(RecordEditInfo editInfo) {
-        ViewGroup root = mContentRoot;
-        View editView = editInfo.getEditView(this, mInflater, root, this);
-        root.addView(mInflater.inflate(R.layout.tag_divider, root, false));
-        root.addView(editView);
-    }
-
-    protected void rebuildChildViews() {
+    public void refresh() {
         ViewGroup root = mContentRoot;
+        View editView = mRecord.getEditView(this, mInflater, root, this);
         root.removeAllViews();
-        for (RecordEditInfo editInfo : mRecords) {
-            addViewForRecord(editInfo);
-        }
-    }
-
-    @Override
-    protected Dialog onCreateDialog(int id, Bundle args) {
-        if (id == DIALOG_ID_ADD_CONTENT) {
-            return new TagContentSelector(this);
-        }
-        return super.onCreateDialog(id, args);
-    }
-
-    @Override
-    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
-        super.onPrepareDialog(id, dialog, args);
-        if (dialog instanceof TagContentSelector) {
-            ((TagContentSelector) dialog).rebuildViews();
-        }
+        root.addView(editView);
     }
 
-    /**
-     * Displays a {@link Dialog} to select a new content type to add to the Tag.
-     */
-    protected void showAddContentDialog() {
-        showDialog(DIALOG_ID_ADD_CONTENT);
-    }
 
     @Override
     public void startPickForRecord(RecordEditInfo editInfo, Intent intent) {
-        mRecordWithOutstandingPick = editInfo;
         startActivityForResult(intent, 0);
     }
 
-    /**
-     * Handles a click to select and add a new content type.
-     */
-    public void onAddContentClick(View target) {
-        Object tag = target.getTag();
-        if ((tag == null) || !(tag instanceof RecordEditInfo)) {
-            return;
-        }
-
-        RecordEditInfo info = (RecordEditInfo) tag;
-        Intent pickIntent = info.getPickIntent();
-        if (pickIntent != null) {
-            startPickForRecord(info, pickIntent);
-        } else {
-            // Does not require an external Activity. Add the edit view directly.
-            addRecord(info);
-        }
-    }
-
     @Override
     public void deleteRecord(RecordEditInfo editInfo) {
-        mRecords.remove(editInfo);
-        rebuildChildViews();
     }
 
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if ((resultCode != RESULT_OK) || (data == null)) {
-            mRecordWithOutstandingPick = null;
-            return;
-        }
-        if (mRecordWithOutstandingPick == null) {
             return;
         }
 
         // Handles results from another Activity that picked content to write to a tag.
-        RecordEditInfo recordInfo = mRecordWithOutstandingPick;
         try {
-            recordInfo.handlePickResult(this, data);
+            mRecord.handlePickResult(this, data);
         } catch (IllegalArgumentException ex) {
-            if (mRecords.contains(recordInfo)) {
-                deleteRecord(recordInfo);
-            }
+            // TODO: handle.
             return;
         }
-
-        if (mRecords.contains(recordInfo)) {
-            // Editing an existing record. Just rebuild everything.
-            rebuildChildViews();
-
-        } else {
-            // Adding a new record.
-            addRecord(recordInfo);
-        }
-        // TODO: handle errors in picking (e.g. the image is too big, etc).
-
-        mRecordWithOutstandingPick = null;
+        refresh();
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-
-        if (mRecordWithOutstandingPick != null) {
-            outState.putParcelable(BUNDLE_KEY_OUTSTANDING_PICK, mRecordWithOutstandingPick);
-        }
+        outState.putParcelable(BUNDLE_KEY_OUTSTANDING_PICK, mRecord);
     }
 
     interface GetTagQuery {
@@ -326,7 +208,7 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
         }
     }
 
-    private void resolveIntent() {
+    protected void resolveIntent() {
         Intent intent = getIntent();
 
         if (Intent.ACTION_SEND.equals(intent.getAction()) && !mParsedIntent) {
@@ -342,8 +224,23 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
         if (uri != null) {
             // Edit existing tag.
             new LoadTagTask().execute(uri);
+        } else {
+            RecordEditInfo newRecord = intent.getParcelableExtra(EXTRA_NEW_RECORD_INFO);
+            if (newRecord != null) {
+                initializeForNewTag(newRecord);
+            }
+        }
+    }
+
+    private void initializeForNewTag(RecordEditInfo editInfo) {
+        mRecord = editInfo;
+
+        Intent pickIntent = editInfo.getPickIntent();
+        if (pickIntent != null) {
+            startPickForRecord(editInfo, pickIntent);
+        } else {
+            refresh();
         }
-        // else, new tag - do nothing.
     }
 
     private void populateFromMessage(NdefMessage refMessage) {
@@ -351,22 +248,14 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
         ParsedNdefMessage parsed = NdefMessageParser.parse(refMessage);
         List<ParsedNdefRecord> records = parsed.getRecords();
 
-        // TODO: loosen this restriction. Just check the type of the first record.
+        // TODO: loosen this restriction.
         // There is always a "Text" record for a My Tag.
-        if (records.size() < 1) {
+        if (records.size() != 1) {
             Log.w(LOG_TAG, "Message not in expected format");
             return;
         }
-        mTextView.setText(((TextRecord) records.get(0)).getText());
-
-        mRecords.clear();
-        for (int i = 1, len = records.size(); i < len; i++) {
-            RecordEditInfo editInfo = records.get(i).getEditInfo(this);
-            if (editInfo != null) {
-                addRecord(editInfo);
-            }
-        }
-        rebuildChildViews();
+        mRecord = records.get(0).getEditInfo(this);
+        refresh();
     }
 
     /**
@@ -378,22 +267,17 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
         String type = intent.getType();
 
         if ("text/plain".equals(type)) {
-            String title = getIntent().getStringExtra(Intent.EXTRA_SUBJECT);
-            mTextView.setText((title == null) ? "" : title);
-
             String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
             try {
                 URL parsed = new URL(text);
 
                 // Valid URL.
-                mTextView.setText("");
-                mRecords.add(new UriRecord.UriRecordEditInfo(text));
-                rebuildChildViews();
+                mRecord = new UriRecord.UriRecordEditInfo(text);
+                refresh();
                 return true;
 
             } catch (MalformedURLException ex) {
-                // Ignore. Just treat as plain text.
-                mTextView.setText((text == null) ? "" : text);
+                // TODO: handle.
             }
 
         } else if ("text/x-vcard".equals(type)) {
@@ -401,15 +285,13 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
             if (stream != null) {
                 RecordEditInfo editInfo = VCardRecord.editInfoForUri(stream);
                 if (editInfo != null) {
-                    mRecords.add(editInfo);
-                    rebuildChildViews();
+                    mRecord = editInfo;
+                    refresh();
                     return true;
                 }
             }
         }
 
-        // TODO: handle images.
-
         return false;
     }
 
@@ -417,14 +299,7 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
      * Saves the content of the tag.
      */
     private void saveAndFinish() {
-        String text = mTextView.getText().toString();
-        Locale locale = getResources().getConfiguration().locale;
-        ArrayList<NdefRecord> values = Lists.newArrayList(
-                TextRecord.newTextRecord(text, locale)
-        );
-
-        values.addAll(getValues());
-        NdefMessage msg = new NdefMessage(values.toArray(new NdefRecord[values.size()]));
+        NdefMessage msg = new NdefMessage(new NdefRecord[] { mRecord.getValue() });
 
         if (Intent.ACTION_SEND.equals(getIntent().getAction())) {
             // If opening directly from a different application via ACTION_SEND, save the tag and
@@ -446,9 +321,6 @@ public class EditTagActivity extends Activity implements OnClickListener, EditCa
     @Override
     public void onClick(View target) {
         switch (target.getId()) {
-            case R.id.add_content_target:
-                showAddContentDialog();
-                break;
             case R.id.save:
                 saveAndFinish();
                 break;
index 3dd67cd..a4759f2 100644 (file)
 
 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.ImageRecord;
+import com.android.apps.tag.record.RecordEditInfo;
+import com.android.apps.tag.record.UriRecord;
+import com.android.apps.tag.record.VCardRecord;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import android.app.Activity;
@@ -40,7 +46,6 @@ import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -58,22 +63,27 @@ import android.widget.Toast;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Set;
 
 /**
  * Displays the list of tags that can be set as "My tag", and allows the user to select the
  * active tag that the device shares.
  */
-public class MyTagList extends Activity implements OnItemClickListener, View.OnClickListener {
+public class MyTagList
+        extends Activity
+        implements OnItemClickListener, View.OnClickListener, SelectContentCallbacks {
 
     static final String TAG = "TagList";
 
     private static final int REQUEST_EDIT = 0;
     private static final int DIALOG_ID_SELECT_ACTIVE_TAG = 0;
+    private static final int DIALOG_ID_ADD_NEW_TAG = 1;
 
     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;
     private CheckBox mEnabled;
@@ -313,9 +323,7 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
                 break;
 
             case R.id.add_tag:
-                // TODO: use implicit intents.
-                Intent intent = new Intent(this, EditTagActivity.class);
-                startActivityForResult(intent, REQUEST_EDIT);
+                showDialog(DIALOG_ID_ADD_NEW_TAG);
                 break;
 
             case R.id.active_tag:
@@ -325,6 +333,21 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
     }
 
     @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
@@ -408,6 +431,8 @@ public class MyTagList extends Activity implements OnItemClickListener, View.OnC
             mSelectActiveTagDialog = new WeakReference<SelectActiveTagDialog>(
                     new SelectActiveTagDialog(this, mAdapter.getCursor()));
             return mSelectActiveTagDialog.get();
+        } else if (id == DIALOG_ID_ADD_NEW_TAG) {
+            return new TagContentSelector(this, this);
         }
         return super.onCreateDialog(id, args);
     }
index e1055a6..3de77fa 100644 (file)
 
 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;
@@ -24,6 +29,8 @@ 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.
@@ -31,17 +38,31 @@ import android.view.ViewGroup;
 public class TagContentSelector extends AlertDialog
         implements DialogInterface.OnClickListener, android.view.View.OnClickListener {
 
-    private final EditTagActivity mActivity;
     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();
 
-    public TagContentSelector(EditTagActivity activity) {
+        /**
+         * Handle a selection of new data for an {@link NdefRecord}.
+         */
+        void onSelectContent(RecordEditInfo info);
+    }
+
+    public TagContentSelector(Activity activity, SelectContentCallbacks callbacks) {
         super(activity);
-        mActivity = activity;
+        mCallbacks = callbacks;
 
         setTitle(activity.getResources().getString(R.string.select_type));
 
-        LayoutInflater inflater = LayoutInflater.from(activity);
-        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tag_content_selector, null);
+        mInflater = LayoutInflater.from(activity);
+        ViewGroup root = (ViewGroup) mInflater.inflate(R.layout.tag_content_selector, null);
         mListRoot = (ViewGroup) root.findViewById(R.id.list);
 
         rebuildViews();
@@ -54,10 +75,24 @@ public class TagContentSelector extends AlertDialog
                 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 : mActivity.getSupportedTypes()) {
-            View selectItemView = mActivity.getAddView(mListRoot, type);
+        for (String type : mCallbacks.getSupportedTypes()) {
+            View selectItemView = getAddView(mListRoot, type);
             if (selectItemView != null) {
                 selectItemView.setOnClickListener(this);
                 mListRoot.addView(selectItemView);
@@ -65,7 +100,6 @@ public class TagContentSelector extends AlertDialog
         }
     }
 
-
     @Override
     public void onClick(DialogInterface dialog, int which) {
         dismiss();
@@ -73,7 +107,11 @@ public class TagContentSelector extends AlertDialog
 
     @Override
     public void onClick(View target) {
-        mActivity.onAddContentClick(target);
+        Object tag = target.getTag();
+        if ((tag == null) || !(tag instanceof RecordEditInfo)) {
+            return;
+        }
+        mCallbacks.onSelectContent((RecordEditInfo) tag);
         dismiss();
     }
 }
index 55e9275..650dba7 100644 (file)
@@ -29,7 +29,7 @@ import android.database.sqlite.SQLiteOpenHelper;
 public class TagDBHelper extends SQLiteOpenHelper {
 
     private static final String DATABASE_NAME = "tags.db";
-    private static final int DATABASE_VERSION = 15;
+    private static final int DATABASE_VERSION = 16;
 
     public static final String TABLE_NAME_NDEF_MESSAGES = "ndef_msgs";
 
index ebaaf57..942b5f2 100644 (file)
@@ -20,28 +20,15 @@ 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.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.media.ThumbnailUtils;
 import android.nfc.NdefRecord;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.MediaStore;
-import android.provider.OpenableColumns;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
 
 import java.io.ByteArrayOutputStream;
-import java.util.List;
 
 /**
  * A NdefRecord corresponding to an image type.
@@ -63,41 +50,6 @@ public class ImageRecord extends ParsedNdefRecord {
         return image;
     }
 
-    @Override
-    public RecordEditInfo getEditInfo(Activity host) {
-        return new ImageRecordEditInfo(mBitmap);
-    }
-
-    private static Intent getPickImageIntent() {
-        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
-        intent.addCategory(Intent.CATEGORY_OPENABLE);
-        intent.setType("image/*");
-        return intent;
-    }
-
-    /**
-     * 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);
-
-        // Determine which Activity can retrieve images.
-        Intent intent = getPickImageIntent();
-        PackageManager pm = context.getPackageManager();
-        List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
-        if (activities.isEmpty()) {
-            return null;
-        }
-
-        ResolveInfo info = activities.get(0);
-        ((ImageView) root.findViewById(R.id.image)).setImageDrawable(info.loadIcon(pm));
-        ((TextView) root.findViewById(R.id.text)).setText(context.getString(R.string.photo));
-
-        root.setTag(new ImageRecordEditInfo());
-        return root;
-    }
-
     public static ImageRecord parse(NdefRecord record) {
         MimeRecord underlyingRecord = MimeRecord.parse(record);
         Preconditions.checkArgument(underlyingRecord.getMimeType().startsWith("image/"));
@@ -126,149 +78,4 @@ public class ImageRecord extends ParsedNdefRecord {
         byte[] content = out.toByteArray();
         return MimeRecord.newMimeRecord("image/jpeg", content);
     }
-
-    private static class ImageRecordEditInfo extends RecordEditInfo {
-        /**
-         * The path on the device where we can load the image. If this is set, the value will be
-         * lazily loaded from the path.
-         */
-        private String mCurrentPath;
-
-        /**
-         * The actual current value of the image for the record.
-         */
-        private Bitmap mCachedValue;
-
-        /**
-         * Pixel size to crop loaded images to.
-         */
-        public static final int MAX_IMAGE_SIZE = 128;
-
-        public ImageRecordEditInfo() {
-            super(RECORD_TYPE);
-            mCurrentPath = "";
-            mCachedValue = null;
-        }
-
-        public ImageRecordEditInfo(String path) {
-            super(RECORD_TYPE);
-            mCurrentPath = path;
-            mCachedValue = null;
-        }
-
-        public ImageRecordEditInfo(Bitmap value) {
-            super(RECORD_TYPE);
-            mCurrentPath = "";
-            mCachedValue = value;
-        }
-
-        protected ImageRecordEditInfo(Parcel parcel) {
-            super(parcel);
-            mCurrentPath = parcel.readString();
-            mCachedValue = parcel.readParcelable(null);
-        }
-
-        @Override
-        public Intent getPickIntent() {
-            return getPickImageIntent();
-        }
-
-        @Override
-        public NdefRecord getValue() {
-            return ImageRecord.newImageRecord(getValueInternal());
-        }
-
-        private Bitmap getValueInternal() {
-            if (mCachedValue == null) {
-                Bitmap original = BitmapFactory.decodeFile(mCurrentPath);
-                int width = original.getWidth();
-                int height = original.getHeight();
-                int major = (width > height) ? width : height;
-                if (major > MAX_IMAGE_SIZE) {
-                    double scale = 1.0 * MAX_IMAGE_SIZE / major;
-                    width *= scale;
-                    height *= scale;
-                }
-                mCachedValue = ThumbnailUtils.extractThumbnail(original, width, height);
-            }
-            return mCachedValue;
-        }
-
-        @Override
-        public void handlePickResult(Context context, Intent data) {
-            Cursor cursor = null;
-            mCachedValue = null;
-
-            try {
-                String[] projection = { MediaStore.Images.Media.DATA, OpenableColumns.SIZE };
-                cursor = context.getContentResolver().query(
-                        data.getData(), projection, null, null, null);
-
-                if (cursor == null) {
-                    Toast.makeText(
-                            context,
-                            context.getResources().getString(R.string.bad_photo),
-                            Toast.LENGTH_LONG).show();
-                    throw new IllegalArgumentException("Selected image could not be loaded");
-                }
-
-                cursor.moveToFirst();
-                int size = cursor.getInt(1);
-                mCurrentPath = cursor.getString(0);
-
-                // TODO: enforce a size limit. May be tricky.
-
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-
-        @Override
-        public View getEditView(
-                Activity activity, LayoutInflater inflater,
-                ViewGroup parent, EditCallbacks callbacks) {
-            View result = buildEditView(
-                    activity, inflater, R.layout.tag_edit_image, parent, callbacks);
-            ((ImageView) result.findViewById(R.id.image)).setImageBitmap(getValueInternal());
-            result.setOnClickListener(this);
-            return result;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeString(mCurrentPath);
-            out.writeParcelable(mCachedValue, flags);
-        }
-
-        @SuppressWarnings("unused")
-        public static final Parcelable.Creator<ImageRecordEditInfo> CREATOR =
-                new Parcelable.Creator<ImageRecordEditInfo>() {
-            @Override
-            public ImageRecordEditInfo createFromParcel(Parcel in) {
-                return new ImageRecordEditInfo(in);
-            }
-
-            @Override
-            public ImageRecordEditInfo[] newArray(int size) {
-                return new ImageRecordEditInfo[size];
-            }
-        };
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void onClick(View target) {
-            if (this == target.getTag()) {
-                mCallbacks.startPickForRecord(this, getPickIntent());
-            } else {
-                super.onClick(target);
-            }
-        }
-    }
 }
index 6b0bf6e..a5ecbbe 100644 (file)
@@ -93,9 +93,7 @@ public abstract class RecordEditInfo implements Parcelable, View.OnClickListener
         result.setTag(this);
 
         View deleteButton = result.findViewById(R.id.delete);
-        if (deleteButton == null) {
-            throw new IllegalArgumentException("All record edit layouts must have a delete button");
-        } else {
+        if (deleteButton != null) {
             deleteButton.setOnClickListener(this);
         }
         mCallbacks = callbacks;
index 3f7e175..e598937 100644 (file)
@@ -292,7 +292,8 @@ public class UriRecord extends ParsedNdefRecord implements OnClickListener {
         }
 
         protected UriRecordEditInfo(Parcel parcel) {
-            this(parcel.readString());
+            super(parcel);
+            mCurrentValue = parcel.readString();
         }
 
         @Override
@@ -302,6 +303,7 @@ public class UriRecord extends ParsedNdefRecord implements OnClickListener {
 
         @Override
         public void handlePickResult(Context context, Intent data) {
+            // Not supported.
         }
 
         @Override
index 362a447..8028600 100644 (file)
@@ -264,7 +264,7 @@ public class VCardRecord extends ParsedNdefRecord implements OnClickListener {
         }
 
         protected VCardRecordEditInfo(Parcel parcel) {
-            super(RECORD_TYPE);
+            super(parcel);
             mLookupUri = parcel.readParcelable(null);
             int valueLength = parcel.readInt();
             if (valueLength > 0) {