Connect photo and name editors, super primary dialog.
This change connects up the photo and display name editors
to show the values from the selected RawContact. This also
persists StructuredName.DISPLAY_NAME changes back.
Also connects up the long-press menu for selecting a
super-primary display name for an aggregate.
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 73ad473..7c53cc3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -714,4 +714,6 @@
secondary contact detail edit fields, such as birthday. -->
<string name="edit_secondary_collapse">Secondary details</string>
+ <string name="dialog_primary_name">Primary name</string>
+
</resources>
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index 8526427..2867662 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -131,7 +131,9 @@
return entry;
}
}
- return null;
+
+ // When no direct primary, return something
+ return mimeEntries.size() > 0 ? mimeEntries.get(0) : null;
}
/**
@@ -390,6 +392,16 @@
}
}
+ public byte[] getAsByteArray(String key) {
+ if (mAfter != null && mAfter.containsKey(key)) {
+ return mAfter.getAsByteArray(key);
+ } else if (mBefore != null && mBefore.containsKey(key)) {
+ return mBefore.getAsByteArray(key);
+ } else {
+ return null;
+ }
+ }
+
public Long getAsLong(String key) {
if (mAfter != null && mAfter.containsKey(key)) {
return mAfter.getAsLong(key);
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index 720882f..8b4442e 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -21,6 +21,7 @@
import com.android.contacts.model.ContactsSource;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.Sources;
+import com.android.contacts.model.ContactsSource.EditType;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.ui.widget.ContactEditorView;
import com.android.internal.widget.ContactHeaderWidget;
@@ -44,7 +45,10 @@
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts.Data;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -52,7 +56,9 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
import android.widget.ImageView;
+import android.widget.ListAdapter;
import android.widget.TextView;
import android.widget.Toast;
@@ -191,7 +197,10 @@
final View tabView = createTabView(mTabWidget, source);
mTabWidget.addTab(tabView);
}
- mTabWidget.setCurrentTab(0);
+ if (mEntities.size() > 0) {
+ mTabWidget.setCurrentTab(0);
+ this.onTabSelectionChanged(0, false);
+ }
}
/**
@@ -243,12 +252,12 @@
/** {@inheritDoc} */
public void onDisplayNameLongClick(View view) {
- // TODO: show dialog to pick primary display name
+ this.createNameDialog().show();
}
/** {@inheritDoc} */
public void onPhotoLongClick(View view) {
- // TODO: show dialog to pick primary photo
+ this.createPhotoDialog().show();
}
@@ -313,7 +322,7 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// show or hide photo item based on current tab
- // hide entirely if on read-only source
+ // hide photo stuff entirely if on read-only source
return true;
}
@@ -343,15 +352,17 @@
final ContentResolver resolver = this.getContentResolver();
boolean savedChanges = false;
for (EntityDelta entity : mEntities) {
- Log.d(TAG, "about to persist " + entity.toString());
-
final ArrayList<ContentProviderOperation> diff = entity.buildDiff();
- savedChanges |= diff.size() > 0;
+
+ // Skip updates that don't change
+ if (diff.size() == 0) continue;
+ savedChanges = true;
// TODO: handle failed operations by re-reading entity
// may also need backoff algorithm to give failed msg after n tries
try {
+ Log.d(TAG, "about to persist " + entity.toString());
resolver.applyBatch(ContactsContract.AUTHORITY, diff);
} catch (RemoteException e) {
Log.w(TAG, "problem writing rawcontact diff", e);
@@ -381,11 +392,10 @@
* user confirmation before continuing.
*/
private boolean doDeleteAction() {
- showDialog(R.id.dialog_delete);
+ this.createDeleteDialog().show();
return true;
}
-
/**
* Delete the entire contact currently being edited.
*/
@@ -429,25 +439,6 @@
-
-
-
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case R.id.dialog_delete:
- return createDeleteDialog();
- case R.id.dialog_photo:
- return createPhotoDialog();
- case R.id.dialog_name:
- return createNameDialog();
- }
- return super.onCreateDialog(id);
- }
-
-
-
private Dialog createDeleteDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.deleteConfirmation_title);
@@ -468,15 +459,59 @@
return null;
}
+ /**
+ * Create dialog for selecting primary display name.
+ */
private Dialog createNameDialog() {
- // TODO: build dialog for picking primary name
- return null;
+ // Build set of all available display names
+ final ArrayList<ValuesDelta> allNames = new ArrayList<ValuesDelta>();
+ for (EntityDelta entity : this.mEntities) {
+ final ArrayList<ValuesDelta> displayNames = entity
+ .getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+ allNames.addAll(displayNames);
+ }
+
+ // Wrap our context to inflate list items using correct theme
+ final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
+ final LayoutInflater dialogInflater = this.getLayoutInflater().cloneInContext(dialogContext);
+
+ final ListAdapter nameAdapter = new ArrayAdapter<ValuesDelta>(this,
+ android.R.layout.simple_list_item_1, allNames) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = dialogInflater.inflate(
+ android.R.layout.simple_expandable_list_item_1, parent, false);
+ }
+
+ final ValuesDelta structuredName = this.getItem(position);
+ final String displayName = structuredName.getAsString(StructuredName.DISPLAY_NAME);
+
+ ((TextView)convertView).setText(displayName);
+
+ return convertView;
+ }
+ };
+
+ final DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ // User picked display name, so make super-primary
+ final ValuesDelta structuredName = allNames.get(which);
+ structuredName.put(Data.IS_PRIMARY, 1);
+ structuredName.put(Data.IS_SUPER_PRIMARY, 1);
+
+ // TODO: include last social snippet after update
+ final String displayName = structuredName.getAsString(StructuredName.DISPLAY_NAME);
+ mHeader.bindStatic(displayName, null);
+ }
+ };
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.dialog_primary_name);
+ builder.setSingleChoiceItems(nameAdapter, 0, clickListener);
+ return builder.create();
}
-
-
-
-
-
-
}
diff --git a/src/com/android/contacts/ui/widget/ContactEditorView.java b/src/com/android/contacts/ui/widget/ContactEditorView.java
index 751e037..ece906d 100644
--- a/src/com/android/contacts/ui/widget/ContactEditorView.java
+++ b/src/com/android/contacts/ui/widget/ContactEditorView.java
@@ -31,6 +31,8 @@
import android.content.DialogInterface;
import android.content.Entity;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
@@ -47,6 +49,7 @@
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.EditText;
+import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
@@ -140,8 +143,8 @@
mDisplayName.setValues(null, primary, state);
} else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
// Handle special case editor for photos
- final ValuesDelta firstValue = state.getPrimaryEntry(mimeType);
- mPhoto.setValues(null, firstValue, state);
+ final ValuesDelta primary = state.getPrimaryEntry(mimeType);
+ mPhoto.setValues(null, primary, state);
} else {
// Otherwise use generic section-based editors
if (kind.fieldList == null) continue;
@@ -500,34 +503,31 @@
protected static class PhotoEditor extends ViewHolder implements Editor {
private static final int RES_PHOTO = R.layout.item_editor_photo;
+ private ImageView mPhoto;
+ private ValuesDelta mEntry;
+
public PhotoEditor(Context context) {
super(context, RES_PHOTO);
+
+ mPhoto = (ImageView)mContent;
}
-// private void setPhotoPresent(boolean present) {
-// mPhotoPresent = present;
-//
-// // Correctly scale the contact photo if present, otherwise just center
-// // the photo placeholder icon.
-// if (mPhotoPresent) {
-// mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
-// } else {
-// mPhotoImageView.setImageResource(R.drawable.ic_menu_add_picture);
-// mPhotoImageView.setScaleType(ImageView.ScaleType.CENTER);
-// }
-//
-// if (mPhotoMenuItem != null) {
-// if (present) {
-// mPhotoMenuItem.setTitle(R.string.removePicture);
-// mPhotoMenuItem.setIcon(android.R.drawable.ic_menu_delete);
-// } else {
-// mPhotoMenuItem.setTitle(R.string.addPicture);
-// mPhotoMenuItem.setIcon(R.drawable.ic_menu_add_picture);
-// }
-// }
-// }
-
public void setValues(DataKind kind, ValuesDelta values, EntityDelta state) {
+ mEntry = values;
+ if (values == null) {
+ // Invalid photo, show default "add photo" placeholder
+ mPhoto.setScaleType(ImageView.ScaleType.CENTER);
+ mPhoto.setImageResource(R.drawable.ic_menu_add_picture);
+ return;
+ }
+
+ // Try decoding photo if actual entry
+ final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
+ final Bitmap photo = BitmapFactory
+ .decodeByteArray(photoBytes, 0, photoBytes.length);
+
+ mPhoto.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ mPhoto.setImageBitmap(photo);
}
public void setEditorListener(EditorListener listener) {
@@ -540,11 +540,37 @@
protected static class DisplayNameEditor extends ViewHolder implements Editor {
private static final int RES_DISPLAY_NAME = R.layout.item_editor_displayname;
+ private EditText mName;
+ private ValuesDelta mEntry;
+
public DisplayNameEditor(Context context) {
super(context, RES_DISPLAY_NAME);
+
+ mName = (EditText)mContent.findViewById(R.id.name);
}
public void setValues(DataKind kind, ValuesDelta values, EntityDelta state) {
+ mEntry = values;
+ if (values == null) {
+ // Invalid display name, so reset and skip
+ mName.setText(null);
+ return;
+ }
+
+ final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
+ mName.setText(displayName);
+ mName.addTextChangedListener(new TextWatcher() {
+ public void afterTextChanged(Editable s) {
+ // Write the newly changed value
+ mEntry.put(StructuredName.DISPLAY_NAME, s.toString());
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+ });
}
public void setEditorListener(EditorListener listener) {