Older EAS types, new Email field, fix count and INSERT bugs.
The EAS sync-adapter isn't ready to handle type-less Email
and IM entries yet, so bring back the original strongly-
typed editors. Partially fixes http://b/2089080
Provide a second edit field for Email.DISPLAY_NAME when
editing EAS contacts. Partially fixes http://b/2092744
Correct count when determining canInsert() for fields that
have typeOverallMax constraints. Fixes http://b/2089132
Correctly build incoming StructuredName using existing
field, and check for empty fields to fix http://b/2078726
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 04707df..e51389c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -856,6 +856,14 @@
<string name="type_radio">Radio</string>
<string name="type_assistant">Assistant</string>
+<string name="type_email_1">Email 1</string>
+<string name="type_email_2">Email 2</string>
+<string name="type_email_3">Email 3</string>
+
+<string name="type_im_1">Im 1</string>
+<string name="type_im_2">Im 2</string>
+<string name="type_im_3">Im 3</string>
+
<string name="type_im_aim">AIM</string>
<string name="type_im_msn">Windows Live</string>
<string name="type_im_yahoo">Yahoo</string>
@@ -888,6 +896,10 @@
and will not be synced. -->
<string name="account_phone">Phone-only (unsynced)</string>
+ <!-- The label describing the display name field of an Email address, allowing the
+ user to give a specific name to describe this address. -->
+ <string name="label_email_display_name">Display name</string>
+
<string name="call_home">Call home</string>
<string name="call_mobile">Call mobile</string>
<string name="call_work">Call work</string>
@@ -898,6 +910,8 @@
<string name="call_custom">Call <xliff:g id="custom">%s</xliff:g></string>
<!-- exchange specific -->
+<string name="call_home_2">Call home 2</string>
+<string name="call_work_2">Call work 2</string>
<string name="call_car">Call car</string>
<string name="call_company_main">Call company main</string>
<string name="call_mms">Call MMS</string>
@@ -917,6 +931,8 @@
<string name="sms_custom">Text <xliff:g id="custom">%s</xliff:g></string>
<!-- exchange specific -->
+<string name="sms_home_2">Text home 2</string>
+<string name="sms_work_2">Text work 2</string>
<string name="sms_car">Text car</string>
<string name="sms_company_main">Text company main</string>
<string name="sms_mms">Text MMS</string>
@@ -930,6 +946,9 @@
<string name="email_other">Email other</string>
<string name="email_custom">Email <xliff:g id="custom">%s</xliff:g></string>
+<string name="email_1">Email 1</string>
+<string name="email_2">Email 2</string>
+<string name="email_3">Email 2</string>
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index 1b99881..cb4a8c9 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -121,13 +121,11 @@
}
/**
- * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY}.
+ * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY},
+ * which may return null when no entry exists.
*/
public ValuesDelta getPrimaryEntry(String mimeType) {
final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
-
- // TODO: handle the case where the caller must have a non-null value,
- // for example inserting a displayname automatically
if (mimeEntries == null) return null;
for (ValuesDelta entry : mimeEntries) {
@@ -157,9 +155,17 @@
return getMimeEntries(mimeType, false);
}
- public int getMimeEntriesCount(String mimeType) {
- final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
- return mimeEntries == null ? 0 : mimeEntries.size();
+ public int getMimeEntriesCount(String mimeType, boolean onlyVisible) {
+ final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType);
+ if (mimeEntries == null) return 0;
+
+ int count = 0;
+ for (ValuesDelta child : mimeEntries) {
+ // Skip deleted items when requesting only visible
+ if (onlyVisible && !child.isVisible()) continue;
+ count++;
+ }
+ return count;
}
public boolean hasMimeEntries(String mimeType) {
@@ -196,12 +202,8 @@
*/
public int getEntryCount(boolean onlyVisible) {
int count = 0;
- for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
- for (ValuesDelta child : mimeEntries) {
- // Skip deleted items when requesting only visible
- if (onlyVisible && child.isVisible()) continue;
- count++;
- }
+ for (String mimeType : mEntries.keySet()) {
+ count += getMimeEntriesCount(mimeType, onlyVisible);
}
return count;
}
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 1c3c8f7..87c8432 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -52,9 +52,10 @@
*/
public static boolean canInsert(EntityDelta state, DataKind kind) {
// Insert possible when have valid types and under overall maximum
+ final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
final boolean validTypes = hasValidTypes(state, kind);
final boolean validOverall = (kind.typeOverallMax == -1)
- || (state.getEntryCount(true) < kind.typeOverallMax);
+ || (visibleCount < kind.typeOverallMax);
return (validTypes && validOverall);
}
@@ -72,7 +73,7 @@
*/
public static void ensureKindExists(EntityDelta state, ContactsSource source, String mimeType) {
final DataKind kind = source.getKindForMimetype(mimeType);
- final boolean hasChild = state.getMimeEntriesCount(mimeType) > 0;
+ final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
if (!hasChild && kind != null) {
// Create child when none exists and valid kind
@@ -202,6 +203,7 @@
* assuming the given {@link DataKind} dictates the possible types.
*/
public static EditType getCurrentType(Cursor cursor, DataKind kind) {
+ if (kind.typeColumn == null) return null;
final int index = cursor.getColumnIndex(kind.typeColumn);
final int rawValue = cursor.getInt(index);
return getType(kind, rawValue);
@@ -319,14 +321,16 @@
{
// StructuredName
- final DataKind kind = source.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
+ final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
+
final String name = extras.getString(Insert.NAME);
+ if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
+ child.put(StructuredName.GIVEN_NAME, name);
+ }
+
final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
- if (kind != null && (TextUtils.isGraphic(name) || TextUtils.isGraphic(phoneticName))) {
- // TODO: handle the case where name already exists and limited to one
- // TODO: represent phonetic name as structured fields
- final ValuesDelta child = EntityModifier.insertChild(state, kind, null);
- child.put(StructuredName.DISPLAY_NAME, name);
+ if (!TextUtils.isEmpty(phoneticName) && TextUtils.isGraphic(phoneticName)) {
child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
}
}
diff --git a/src/com/android/contacts/model/HardCodedSources.java b/src/com/android/contacts/model/HardCodedSources.java
index d9fd83c..07e5877 100644
--- a/src/com/android/contacts/model/HardCodedSources.java
+++ b/src/com/android/contacts/model/HardCodedSources.java
@@ -369,26 +369,37 @@
// TODO: this should come from resource in the future
private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
- public static final ValuesDelta buildMyContactsMembership(Context context) {
+ public static final void attemptMyContactsMembership(EntityDelta state, Context context) {
final ContentResolver resolver = context.getContentResolver();
final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups.SOURCE_ID },
Groups.TITLE + "=?", new String[] { GOOGLE_MY_CONTACTS_GROUP }, null);
-
- final ContentValues values = new ContentValues();
- if (cursor.moveToFirst()) {
- final String sourceId = cursor.getString(0);
- values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
- values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
+ try {
+ if (cursor.moveToFirst()) {
+ final ContentValues values = new ContentValues();
+ final String sourceId = cursor.getString(0);
+ values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+ values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
+ state.addEntry(ValuesDelta.fromAfter(values));
+ }
+ } finally {
+ cursor.close();
}
-
- cursor.close();
- return ValuesDelta.fromAfter(values);
}
/**
* The constants below are shared with the Exchange sync adapter, and are
* currently static. These values should be maintained in parallel.
*/
+ private static final int TYPE_EMAIL1 = 20;
+ private static final int TYPE_EMAIL2 = 21;
+ private static final int TYPE_EMAIL3 = 22;
+
+ private static final int TYPE_IM1 = 23;
+ private static final int TYPE_IM2 = 24;
+ private static final int TYPE_IM3 = 25;
+
+ private static final int TYPE_WORK2 = 26;
+ private static final int TYPE_HOME2 = 27;
private static final int TYPE_CAR = 28;
private static final int TYPE_COMPANY_MAIN = 29;
private static final int TYPE_MMS = 30;
@@ -443,11 +454,15 @@
kind.typeColumn = Phone.TYPE;
kind.typeList = Lists.newArrayList();
kind.typeList.add(new EditType(Phone.TYPE_HOME, R.string.type_home, R.string.call_home,
- R.string.sms_home).setSpecificMax(2));
+ R.string.sms_home).setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_HOME2, R.string.type_home_2, R.string.call_home_2,
+ R.string.sms_home_2).setSecondary(true).setSpecificMax(1));
kind.typeList.add(new EditType(Phone.TYPE_MOBILE, R.string.type_mobile,
R.string.call_mobile, R.string.sms_mobile).setSpecificMax(1));
kind.typeList.add(new EditType(Phone.TYPE_WORK, R.string.type_work, R.string.call_work,
- R.string.sms_work).setSpecificMax(2));
+ R.string.sms_work).setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_WORK2, R.string.type_work_2, R.string.call_work_2,
+ R.string.sms_work_2).setSecondary(true).setSpecificMax(1));
kind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, R.string.type_fax_work,
R.string.call_fax_work, R.string.sms_fax_work).setSecondary(true)
.setSpecificMax(1));
@@ -482,10 +497,20 @@
kind.actionHeader = new ActionInflater(list.resPackageName, kind);
kind.actionBody = new SimpleInflater(Email.DATA);
- kind.typeOverallMax = 3;
+
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(new EditType(TYPE_EMAIL1, R.string.type_email_1, R.string.email_1)
+ .setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_EMAIL2, R.string.type_email_2, R.string.email_2)
+ .setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_EMAIL3, R.string.type_email_3, R.string.email_3)
+ .setSpecificMax(1));
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+ kind.fieldList.add(new EditField(Email.DISPLAY_NAME, R.string.label_email_display_name,
+ FLAGS_PERSON_NAME));
list.add(kind);
}
@@ -497,7 +522,12 @@
kind.actionHeader = new ActionInflater(list.resPackageName, kind);
kind.actionBody = new SimpleInflater(Im.DATA);
- kind.typeOverallMax = 3;
+
+ kind.typeColumn = Im.TYPE;
+ kind.typeList = new ArrayList<EditType>();
+ kind.typeList.add(new EditType(TYPE_IM1, R.string.type_im_1).setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_IM2, R.string.type_im_2).setSpecificMax(1));
+ kind.typeList.add(new EditType(TYPE_IM3, R.string.type_im_3).setSpecificMax(1));
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index d6acf30..60dc19b 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -730,9 +730,7 @@
// Create "My Contacts" membership for Google contacts
// TODO: move this off into "templates" for each given source
if (HardCodedSources.ACCOUNT_TYPE_GOOGLE.equals(source.accountType)) {
- final ValuesDelta membership = HardCodedSources
- .buildMyContactsMembership(target);
- insert.addEntry(membership);
+ HardCodedSources.attemptMyContactsMembership(insert, target);
}
target.mState.add(insert);