Follow up to EditSchema parser
- Now AccountType.addKind() throws DefinitionException instead of just logging.
- Now the test contacts.xml (= test_basic_contacts.xml) defines "event" and
"relationship" DataKinds too. BUA should be able to copy this file.
- Added another xml, contacts_fallback.xml, to the test apk.
This defines what's equivalent to the fallback type. Unittests load this file
directly and compares the result to the fallback account type.
- Cleaned up existing account definitions in order to make sure
contacts_fallback.xml is really identical to the fallback type. This includes:
** Now structured name, display name, phonetic name, and phone DataKinds
all have 'kind.typeOverallMax = 1'.
** The "assistant" phone type is no longer a custom column. It's only used for
the fallback type and I don't think it's too critical.
- Also, NameKindBuilder no longer re-order phonetic fields, because no
other account types do this. In the previous CL I did it because I thought
that'd be more "correct", but on the second thought it's probably not a good
idea to make too many non-critical changes at this point.
Bug 5381810
Change-Id: Ie6a4eb3b876ab22a3dcdb6a9c278e387f8166125
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 654adeb..23d39d3 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -32,7 +32,6 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
@@ -83,11 +82,35 @@
*/
private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
+ protected boolean mIsInitialized;
+
+ protected static class DefinitionException extends Exception {
+ public DefinitionException(String message) {
+ super(message);
+ }
+
+ public DefinitionException(String message, Exception inner) {
+ super(message, inner);
+ }
+ }
+
/**
* Whether this account type was able to be fully initialized. This may be false if
* (for example) the package name associated with the account type could not be found.
*/
- public boolean isInitialized() {
+ public final boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ /**
+ * @return Whether this type is an "embedded" type. i.e. any of {@link FallbackAccountType},
+ * {@link GoogleAccountType} or {@link ExternalAccountType}.
+ *
+ * If an embedded type cannot be initialized (i.e. if {@link #isInitialized()} returns
+ * {@code false}) it's considered critical, and the application will crash. On the other
+ * hand if it's not an embedded type, we just skip loading the type.
+ */
+ public boolean isEmbedded() {
return true;
}
@@ -274,10 +297,10 @@
/**
* Add given {@link DataKind} to list of those provided by this source.
*/
- public DataKind addKind(DataKind kind) {
+ public DataKind addKind(DataKind kind) throws DefinitionException {
if (mMimeKinds.get(kind.mimeType) != null) {
- // TODO Make it exception.
- Log.w(TAG, "mime type '" + kind.mimeType + "' is already registered");
+ throw new DefinitionException(
+ "mime type '" + kind.mimeType + "' is already registered");
}
kind.resPackageName = this.resPackageName;
@@ -337,6 +360,16 @@
public int hashCode() {
return rawValue;
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " rawValue=" + rawValue
+ + " labelRes=" + labelRes
+ + " secondary=" + secondary
+ + " specificMax=" + specificMax
+ + " customColumn=" + customColumn;
+ }
}
public static class EventEditType extends EditType {
@@ -354,6 +387,11 @@
mYearOptional = yearOptional;
return this;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + " mYearOptional=" + mYearOptional;
+ }
}
/**
@@ -403,6 +441,19 @@
public boolean isMultiLine() {
return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
}
+
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ":"
+ + " column=" + column
+ + " titleRes=" + titleRes
+ + " inputType=" + inputType
+ + " minLines=" + minLines
+ + " optional=" + optional
+ + " shortForm=" + shortForm
+ + " longForm=" + longForm;
+ }
}
/**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 92323fa..43bb579 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -411,7 +411,12 @@
Log.d(TAG, "Registering external account type=" + type
+ ", packageName=" + auth.packageName);
accountType = new ExternalAccountType(mContext, auth.packageName, false);
- if (!accountType.isInitialized()) {
+ }
+ if (!accountType.isInitialized()) {
+ if (accountType.isEmbedded()) {
+ throw new IllegalStateException("Problem initializing embedded type "
+ + accountType.getClass().getCanonicalName());
+ } else {
// Skip external account types that couldn't be initialized.
continue;
}
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 34beca2..0d766da 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -107,16 +108,6 @@
static final int GROUP_MEMBERSHIP = 999;
}
- protected static class DefinitionException extends Exception {
- public DefinitionException(String message) {
- super(message);
- }
-
- public DefinitionException(String message, Exception inner) {
- super(message, inner);
- }
- }
-
public BaseAccountType() {
this.accountType = null;
this.dataSet = null;
@@ -148,11 +139,12 @@
return new EditType(type, Relation.getTypeLabelResource(type));
}
- protected DataKind addDataKindStructuredName(Context context) {
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -177,11 +169,12 @@
return kind;
}
- protected DataKind addDataKindDisplayName(Context context) {
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -217,11 +210,12 @@
return kind;
}
- protected DataKind addDataKindPhoneticName(Context context) {
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
@@ -236,7 +230,7 @@
return kind;
}
- protected DataKind addDataKindNickname(Context context) {
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
R.string.nicknameLabelsGroup, 115, true, R.layout.text_fields_editor_view));
kind.typeOverallMax = 1;
@@ -252,7 +246,7 @@
return kind;
}
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
10, true, R.layout.text_fields_editor_view));
kind.iconAltRes = R.drawable.ic_text_holo_light;
@@ -262,8 +256,8 @@
kind.actionBody = new SimpleInflater(Phone.NUMBER);
kind.typeColumn = Phone.TYPE;
kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
@@ -282,8 +276,7 @@
kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true).setCustomColumn(
- Phone.LABEL));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
kind.fieldList = Lists.newArrayList();
@@ -292,7 +285,7 @@
return kind;
}
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
15, true, R.layout.text_fields_editor_view));
kind.actionHeader = new EmailActionInflater();
@@ -312,7 +305,7 @@
return kind;
}
- protected DataKind addDataKindStructuredPostal(Context context) {
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
R.string.postalLabelsGroup, 25, true, R.layout.text_fields_editor_view));
kind.actionHeader = new PostalActionInflater();
@@ -333,7 +326,7 @@
return kind;
}
- protected DataKind addDataKindIm(Context context) {
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, 20, true,
R.layout.text_fields_editor_view));
kind.actionHeader = new ImActionInflater();
@@ -364,7 +357,7 @@
return kind;
}
- protected DataKind addDataKindOrganization(Context context) {
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
R.string.organizationLabelsGroup, 5, true,
R.layout.text_fields_editor_view));
@@ -381,14 +374,15 @@
return kind;
}
- protected DataKind addDataKindPhoto(Context context) {
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, true, -1));
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
return kind;
}
- protected DataKind addDataKindNote(Context context) {
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
R.string.label_notes, 110, true, R.layout.text_fields_editor_view));
kind.typeOverallMax = 1;
@@ -400,7 +394,7 @@
return kind;
}
- protected DataKind addDataKindWebsite(Context context) {
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
R.string.websiteLabelsGroup, 120, true, R.layout.text_fields_editor_view));
kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
@@ -414,7 +408,7 @@
return kind;
}
- protected DataKind addDataKindSipAddress(Context context) {
+ protected DataKind addDataKindSipAddress(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
R.string.label_sip_address, 130, true, R.layout.text_fields_editor_view));
@@ -428,7 +422,7 @@
return kind;
}
- protected DataKind addDataKindGroupMembership(Context context) {
+ protected DataKind addDataKindGroupMembership(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
R.string.groupsLabel, 999, true, -1));
@@ -496,6 +490,13 @@
return null;
}
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " mStringRes=" + mStringRes
+ + " mColumnName" + mColumnName;
+ }
}
public static abstract class CommonInflater implements StringInflater {
@@ -535,6 +536,11 @@
final String label = values.getAsString(getLabelColumn());
return getTypeLabel(context.getResources(), type, label);
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
}
public static class PhoneActionInflater extends CommonInflater {
@@ -801,15 +807,14 @@
kind.actionBody = actionBody;
kind.fieldList = Lists.newArrayList();
- kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
-
- // Handle "types".
- // If a kind has the type column, contacts.xml must have at least one type definition.
- // Otherwise, it mustn't have a type definition.
- //
- // If it's a pseudo data kind (== data kind that doesn't have the corresponding
- // DataKind tag in the XML), we just skip this process.
+ // Get more information from the tag...
+ // A pseudo data kind doesn't have corresponding tag the XML, so we skip this.
if (!isPseudo) {
+ kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
+
+ // Process "Type" tags.
+ // If a kind has the type column, contacts.xml must have at least one type
+ // definition. Otherwise, it mustn't have a type definition.
if (kind.typeColumn != null) {
// Parse and add types.
kind.typeList = Lists.newArrayList();
@@ -957,7 +962,6 @@
throwIfList(ks);
kinds.add(ks);
-
// Note about setLongForm/setShortForm below.
// We need to set this only when the type supports display name. (=supportsDisplayName)
// Otherwise (i.e. Exchange) we don't set these flags, but instead make some fields
@@ -988,6 +992,7 @@
R.string.nameLabelsGroup, Weight.NONE, R.layout.text_fields_editor_view,
new SimpleInflater(R.string.nameLabelsGroup),
new SimpleInflater(Nickname.NAME));
+ kd.typeOverallMax = 1;
kinds.add(kd);
kd.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -1023,25 +1028,18 @@
R.string.name_phonetic, Weight.NONE, R.layout.phonetic_name_editor_view,
new SimpleInflater(R.string.nameLabelsGroup),
new SimpleInflater(Nickname.NAME));
+ kp.typeOverallMax = 1;
kinds.add(kp);
+ // We may want to change the order depending on displayOrderPrimary too.
kp.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
R.string.name_phonetic, FLAGS_PHONETIC).setShortForm(true));
- if (!displayOrderPrimary) {
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
- } else {
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
- }
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
+ R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
return kinds;
}
}
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index aaf7bb5..857f3e4 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -20,6 +20,7 @@
import com.android.contacts.model.AccountType.EditField;
import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.AccountType.StringInflater;
+import com.google.common.collect.Iterators;
import android.content.ContentValues;
import android.provider.ContactsContract.Data;
@@ -95,4 +96,43 @@
this.typeOverallMax = -1;
this.editorLayoutResourceId = editorLayoutResourceId;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DataKind:");
+ sb.append(" resPackageName=").append(resPackageName);
+ sb.append(" mimeType=").append(mimeType);
+ sb.append(" titleRes=").append(titleRes);
+ sb.append(" iconAltRes=").append(iconAltRes);
+ sb.append(" iconAltDescriptionRes=").append(iconAltDescriptionRes);
+ sb.append(" weight=").append(weight);
+ sb.append(" editable=").append(editable);
+ sb.append(" actionHeader=").append(actionHeader);
+ sb.append(" actionAltHeader=").append(actionAltHeader);
+ sb.append(" actionBody=").append(actionBody);
+ sb.append(" actionBodySocial=").append(actionBodySocial);
+ sb.append(" typeColumn=").append(typeColumn);
+ sb.append(" typeOverallMax=").append(typeOverallMax);
+ sb.append(" typeList=").append(toString(typeList));
+ sb.append(" fieldList=").append(toString(fieldList));
+ sb.append(" defaultValues=").append(defaultValues);
+ sb.append(" editorLayoutResourceId=").append(editorLayoutResourceId);
+ sb.append(" dateFormatWithoutYear=").append(toString(dateFormatWithoutYear));
+ sb.append(" dateFormatWithYear=").append(toString(dateFormatWithYear));
+
+ return sb.toString();
+ }
+
+ public static String toString(SimpleDateFormat format) {
+ return format == null ? "(null)" : format.toPattern();
+ }
+
+ public static String toString(Iterable<?> list) {
+ if (list == null) {
+ return "(null)";
+ } else {
+ return Iterators.toString(list.iterator());
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index bb11cf6..e5491d2 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
@@ -33,10 +34,12 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.util.Log;
import java.util.Locale;
public class ExchangeAccountType extends BaseAccountType {
+ private static final String TAG = "ExchangeAccountType";
public static final String ACCOUNT_TYPE = "com.android.exchange";
@@ -45,24 +48,30 @@
this.resPackageName = null;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindEvent(context);
- addDataKindWebsite(context);
- addDataKindGroupMembership(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindEvent(context);
+ addDataKindWebsite(context);
+ addDataKindGroupMembership(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
}
@Override
- protected DataKind addDataKindStructuredName(Context context) {
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -91,7 +100,7 @@
}
@Override
- protected DataKind addDataKindDisplayName(Context context) {
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
@@ -124,7 +133,7 @@
}
@Override
- protected DataKind addDataKindPhoneticName(Context context) {
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -142,7 +151,7 @@
}
@Override
- protected DataKind addDataKindNickname(Context context) {
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindNickname(context);
kind.typeOverallMax = 1;
@@ -155,7 +164,7 @@
}
@Override
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhone(context);
kind.typeColumn = Phone.TYPE;
@@ -185,7 +194,7 @@
}
@Override
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindEmail(context);
kind.typeOverallMax = 3;
@@ -197,7 +206,7 @@
}
@Override
- protected DataKind addDataKindStructuredPostal(Context context) {
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindStructuredPostal(context);
final boolean useJapaneseOrder =
@@ -237,7 +246,7 @@
}
@Override
- protected DataKind addDataKindIm(Context context) {
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindIm(context);
// Types are not supported for IM. There can be 3 IMs, but OWA only shows only the first
@@ -253,7 +262,7 @@
}
@Override
- protected DataKind addDataKindOrganization(Context context) {
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindOrganization(context);
kind.typeOverallMax = 1;
@@ -268,7 +277,7 @@
}
@Override
- protected DataKind addDataKindPhoto(Context context) {
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhoto(context);
kind.typeOverallMax = 1;
@@ -280,7 +289,7 @@
}
@Override
- protected DataKind addDataKindNote(Context context) {
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindNote(context);
kind.fieldList = Lists.newArrayList();
@@ -289,7 +298,7 @@
return kind;
}
- protected DataKind addDataKindEvent(Context context) {
+ protected DataKind addDataKindEvent(Context context) throws DefinitionException {
DataKind kind = addKind(
new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, 150, true,
R.layout.event_field_editor_view));
@@ -311,7 +320,7 @@
}
@Override
- protected DataKind addDataKindWebsite(Context context) {
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindWebsite(context);
kind.typeOverallMax = 1;
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 968993a..4eaa976 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -16,8 +16,6 @@
package com.android.contacts.model;
-import com.android.contacts.R;
-import com.android.contacts.model.BaseAccountType.DefinitionException;
import com.google.common.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
@@ -88,30 +86,49 @@
private List<String> mExtensionPackageNames;
private String mAccountTypeLabelAttribute;
private String mAccountTypeIconAttribute;
- private boolean mInitSuccessful;
private boolean mHasContactsMetadata;
private boolean mHasEditSchema;
public ExternalAccountType(Context context, String resPackageName, boolean isExtension) {
+ this(context, resPackageName, isExtension, null);
+ }
+
+ /**
+ * Constructor used for testing to initialize with any arbitrary XML.
+ *
+ * @param injectedMetadata If non-null, it'll be used to initialize the type. Only set by
+ * tests. If null, the metadata is loaded from the specified package.
+ */
+ ExternalAccountType(Context context, String resPackageName, boolean isExtension,
+ XmlResourceParser injectedMetadata) {
this.mIsExtension = isExtension;
this.resPackageName = resPackageName;
this.summaryResPackageName = resPackageName;
- // Handle unknown sources by searching their package
final PackageManager pm = context.getPackageManager();
- XmlResourceParser parser = null;
+ final XmlResourceParser parser;
+ if (injectedMetadata == null) {
+ try {
+ parser = loadContactsXml(context, resPackageName);
+ } catch (NameNotFoundException e1) {
+ // If the package name is not found, we can't initialize this account type.
+ return;
+ }
+ } else {
+ parser = injectedMetadata;
+ }
try {
- PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
- PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
- for (ServiceInfo serviceInfo : packageInfo.services) {
- parser = serviceInfo.loadXmlMetaData(pm,
- METADATA_CONTACTS);
- if (parser == null) continue;
+ if (parser != null) {
inflate(context, parser);
}
- } catch (NameNotFoundException nnfe) {
- // If the package name is not found, we can't initialize this account type.
- return;
+
+ if (!mHasEditSchema) {
+ // Bring in name and photo from fallback source, which are non-optional
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindPhoto(context);
+ }
} catch (DefinitionException e) {
String message = "Problem reading XML";
if (parser != null) {
@@ -135,16 +152,42 @@
iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
this.resPackageName, ATTR_ACCOUNT_ICON);
- if (!mHasEditSchema) {
- // Bring in name and photo from fallback source, which are non-optional
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindPhoto(context);
- }
-
// If we reach this point, the account type has been successfully initialized.
- mInitSuccessful = true;
+ mIsInitialized = true;
+ }
+
+ /**
+ * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package.
+ *
+ * Unfortunately, there's no public way to determine which service defines a sync service for
+ * which account type, so this method looks through all services in the package, and just
+ * returns the first CONTACTS_STRUCTURE metadata defined in any of them.
+ *
+ * Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata. In this case
+ * the account type *will* be initialized with minimal configuration.
+ *
+ * On the other hand, if the package is not found, it throws a {@link NameNotFoundException},
+ * in which case the account type will *not* be initialized.
+ */
+ private XmlResourceParser loadContactsXml(Context context, String resPackageName)
+ throws NameNotFoundException {
+ final PackageManager pm = context.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
+ PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
+ for (ServiceInfo serviceInfo : packageInfo.services) {
+ final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
+ METADATA_CONTACTS);
+ if (parser != null) {
+ return parser;
+ }
+ }
+ // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata.
+ return null;
+ }
+
+ @Override
+ public boolean isEmbedded() {
+ return false;
}
@Override
@@ -153,11 +196,6 @@
}
@Override
- public boolean isInitialized() {
- return mInitSuccessful;
- }
-
- @Override
public boolean areContactsWritable() {
return mHasEditSchema;
}
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index 216d6d0..d81f2f5 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -19,31 +19,52 @@
import com.android.contacts.R;
import android.content.Context;
+import android.util.Log;
public class FallbackAccountType extends BaseAccountType {
+ private static final String TAG = "FallbackAccountType";
- public FallbackAccountType(Context context) {
+ private FallbackAccountType(Context context, String resPackageName) {
this.accountType = null;
this.dataSet = null;
this.titleRes = R.string.account_phone;
this.iconRes = R.mipmap.ic_launcher_contacts;
- this.resPackageName = null;
+ this.resPackageName = resPackageName;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindWebsite(context);
- addDataKindSipAddress(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ public FallbackAccountType(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Used to compare with an {@link ExternalAccountType} built from a test contacts.xml.
+ * In order to build {@link DataKind}s with the same resource package name,
+ * {@code resPackageName} is injectable.
+ */
+ static AccountType createForTest(Context context, String resPackageName) {
+ return new FallbackAccountType(context, resPackageName);
}
@Override
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index c101602..822d829 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
@@ -26,10 +27,13 @@
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.util.Log;
import java.util.List;
public class GoogleAccountType extends BaseAccountType {
+ private static final String TAG = "GoogleAccountType";
+
public static final String ACCOUNT_TYPE = "com.google";
private static final List<String> mExtensionPackages =
@@ -40,22 +44,28 @@
this.resPackageName = null;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindWebsite(context);
- addDataKindSipAddress(context);
- addDataKindGroupMembership(context);
- addDataKindRelation(context);
- addDataKindEvent(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ addDataKindGroupMembership(context);
+ addDataKindRelation(context);
+ addDataKindEvent(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
}
@Override
@@ -64,7 +74,7 @@
}
@Override
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhone(context);
kind.typeColumn = Phone.TYPE;
@@ -87,7 +97,7 @@
}
@Override
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindEmail(context);
kind.typeColumn = Email.TYPE;
@@ -104,7 +114,7 @@
return kind;
}
- private DataKind addDataKindRelation(Context context) {
+ private DataKind addDataKindRelation(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
R.string.relationLabelsGroup, 160, true, R.layout.text_fields_editor_view));
kind.actionHeader = new RelationActionInflater();
@@ -139,7 +149,7 @@
return kind;
}
- private DataKind addDataKindEvent(Context context) {
+ private DataKind addDataKindEvent(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
R.string.eventLabelsGroup, 150, true, R.layout.event_field_editor_view));
kind.actionHeader = new EventActionInflater();
diff --git a/tests/res/xml/contacts_fallback.xml b/tests/res/xml/contacts_fallback.xml
new file mode 100644
index 0000000..ae262eb
--- /dev/null
+++ b/tests/res/xml/contacts_fallback.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2011, 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.
+ */
+-->
+
+<!--
+ contacts.xml to build "fallback account type" equivalent.
+ This is directly used in ExternalAccountTypeTest to test the parser. There's no sync adapter
+ that actually defined with this definition.
+-->
+
+<ContactsAccountType
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ >
+ <EditSchema
+ >
+ <DataKind kind="name"
+ maxOccurs="1"
+ supportsDisplayName="true"
+ supportsPrefix="true"
+ supportsMiddleName="true"
+ supportsSuffix="true"
+ supportsPhoneticFamilyName="true"
+ supportsPhoneticMiddleName="true"
+ supportsPhoneticGivenName="true"
+ >
+ </DataKind>
+ <DataKind kind="photo" maxOccurs="1" />
+ <DataKind kind="phone" >
+ <Type type="mobile" />
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="fax_work" />
+ <Type type="fax_home" />
+ <Type type="pager" />
+ <Type type="other" />
+ <Type type="custom"/>
+ <Type type="callback" />
+ <Type type="car" />
+ <Type type="company_main" />
+ <Type type="isdn" />
+ <Type type="main" />
+ <Type type="other_fax" />
+ <Type type="radio" />
+ <Type type="telex" />
+ <Type type="tty_tdd" />
+ <Type type="work_mobile"/>
+ <Type type="work_pager" />
+ <Type type="assistant" />
+ <Type type="mms" />
+ </DataKind>
+ <DataKind kind="email" >
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="other" />
+ <Type type="mobile" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="nickname" maxOccurs="1" />
+ <DataKind kind="im" >
+ <Type type="aim" />
+ <Type type="msn" />
+ <Type type="yahoo" />
+ <Type type="skype" />
+ <Type type="qq" />
+ <Type type="google_talk" />
+ <Type type="icq" />
+ <Type type="jabber" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="postal" needsStructured="false" >
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="other" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="organization" maxOccurs="1" />
+ <DataKind kind="website" />
+ <DataKind kind="sip_address" maxOccurs="1" />
+ <DataKind kind="note" maxOccurs="1" />
+ </EditSchema>
+</ContactsAccountType>
diff --git a/tests/res/xml/test_basic_contacts.xml b/tests/res/xml/test_basic_contacts.xml
index ad82706..0047204 100644
--- a/tests/res/xml/test_basic_contacts.xml
+++ b/tests/res/xml/test_basic_contacts.xml
@@ -17,10 +17,6 @@
*/
-->
-<!--
- contacts.xml to build "fallback account type" equivalent.
--->
-
<ContactsAccountType
xmlns:android="http://schemas.android.com/apk/res/android"
>
@@ -98,8 +94,8 @@
<DataKind kind="phone" >
<!-- Note: Google type doesn't have obsolete ones -->
<Type type="mobile" />
- <Type type="work" />
<Type type="home" />
+ <Type type="work" />
<Type type="fax_work" />
<Type type="fax_home" />
<Type type="pager" />
@@ -139,9 +135,9 @@
<!--
Email
-->
- <!-- Fallback/ Google definition. -->
+ <!-- Fallback/Google definition. -->
<DataKind kind="email" >
- <!-- Note: Google type doesn't support some of these. -->
+ <!-- Note: Google type doesn't have obsolete ones -->
<Type type="home" />
<Type type="work" />
<Type type="other" />
@@ -248,17 +244,13 @@
<!--
Event
-
- The parser should be able to handle it, but not tested.
-->
- <!-- Google definition.
<DataKind kind="event" dateWithTime="false">
<Type type="birthday" maxOccurs="1" yearOptional="true" />
<Type type="anniversary" />
<Type type="other" />
<Type type="custom" />
</DataKind>
- -->
<!--
Exchange definition. dateWithTime is needed only for Exchange.
@@ -268,11 +260,9 @@
-->
<!--
- Relationship.
-
- The parser should be able to handle it, but not tested.
-
- <DataKind kind="relation" >
+ Relationship
+ -->
+ <DataKind kind="relationship" >
<Type type="assistant" />
<Type type="brother" />
<Type type="child" />
@@ -289,8 +279,5 @@
<Type type="spouse" />
<Type type="custom" />
</DataKind>
- -->
-
</EditSchema>
-
</ContactsAccountType>
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 4db73b3..6872604 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,8 +20,6 @@
import static android.content.ContentProviderOperation.TYPE_INSERT;
import static android.content.ContentProviderOperation.TYPE_UPDATE;
-import com.google.android.collect.Lists;
-
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.AccountTypeManager;
@@ -35,10 +33,10 @@
import com.android.contacts.tests.mocks.ContactsMockContext;
import com.android.contacts.tests.mocks.MockAccountTypeManager;
import com.android.contacts.tests.mocks.MockContentProvider;
+import com.google.android.collect.Lists;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Entity;
import android.net.Uri;
import android.os.Bundle;
@@ -87,56 +85,60 @@
public static class MockContactsSource extends AccountType {
MockContactsSource() {
- this.accountType = TEST_ACCOUNT_TYPE;
+ try {
+ this.accountType = TEST_ACCOUNT_TYPE;
- final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, true, -1);
- nameKind.typeOverallMax = 1;
- addKind(nameKind);
+ final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+ R.string.nameLabelsGroup, -1, true, -1);
+ nameKind.typeOverallMax = 1;
+ addKind(nameKind);
- // Phone allows maximum 2 home, 1 work, and unlimited other, with
- // constraint of 5 numbers maximum.
- final DataKind phoneKind = new DataKind(
- Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+ // Phone allows maximum 2 home, 1 work, and unlimited other, with
+ // constraint of 5 numbers maximum.
+ final DataKind phoneKind = new DataKind(
+ Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1);
- phoneKind.typeOverallMax = 5;
- phoneKind.typeColumn = Phone.TYPE;
- phoneKind.typeList = Lists.newArrayList();
- phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
- phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
- phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
- phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
+ phoneKind.typeOverallMax = 5;
+ phoneKind.typeColumn = Phone.TYPE;
+ phoneKind.typeList = Lists.newArrayList();
+ phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
- phoneKind.fieldList = Lists.newArrayList();
- phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
- phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
+ phoneKind.fieldList = Lists.newArrayList();
+ phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
+ phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
- addKind(phoneKind);
+ addKind(phoneKind);
- // Email is unlimited
- final DataKind emailKind = new DataKind(
- Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
- emailKind.typeOverallMax = -1;
- emailKind.fieldList = Lists.newArrayList();
- emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
- addKind(emailKind);
+ // Email is unlimited
+ final DataKind emailKind = new DataKind(
+ Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+ emailKind.typeOverallMax = -1;
+ emailKind.fieldList = Lists.newArrayList();
+ emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
+ addKind(emailKind);
- // IM is only one
- final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
- true, -1);
- imKind.typeOverallMax = 1;
- imKind.fieldList = Lists.newArrayList();
- imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
- addKind(imKind);
+ // IM is only one
+ final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
+ true, -1);
+ imKind.typeOverallMax = 1;
+ imKind.fieldList = Lists.newArrayList();
+ imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
+ addKind(imKind);
- // Organization is only one
- final DataKind orgKind = new DataKind(
- Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1);
- orgKind.typeOverallMax = 1;
- orgKind.fieldList = Lists.newArrayList();
- orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
- orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
- addKind(orgKind);
+ // Organization is only one
+ final DataKind orgKind = new DataKind(
+ Organization.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+ orgKind.typeOverallMax = 1;
+ orgKind.fieldList = Lists.newArrayList();
+ orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
+ orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
+ addKind(orgKind);
+ } catch (DefinitionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
index 15a1320..ba3d0eb 100644
--- a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
@@ -19,12 +19,15 @@
import com.android.contacts.tests.R;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.XmlResourceParser;
import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
@@ -32,6 +35,10 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.List;
+
+import libcore.util.Objects;
+
/**
* Test case for {@link ExternalAccountType}.
*
@@ -40,7 +47,6 @@
*/
@SmallTest
public class ExternalAccountTypeTest extends AndroidTestCase {
-
public void testResolveExternalResId() {
final Context c = getContext();
// In this test we use the test package itself as an external package.
@@ -63,12 +69,36 @@
"@string/test_string", packageName, ""));
}
+ /**
+ * Initialize with an invalid package name and see if type type will *not* be initialized.
+ */
+ public void testNoPackage() {
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ "!!!no such package name!!!", false);
+ assertFalse(type.isInitialized());
+ }
+
+ /**
+ * Initialize with the name of an existing package, which has no contacts.xml metadata.
+ */
+ public void testNoMetadata() {
+ // Use the main application package, which does exist, but has no contacts.xml in it.
+ String packageName = getContext().getPackageName();
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ packageName, false);
+ assertTrue(type.isInitialized());
+ }
+
+ /**
+ * Initialize with the test package itself and see if EditSchema is correctly parsed.
+ */
public void testEditSchema() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false);
assertTrue(type.isInitialized());
+ // Let's just check if the DataKinds are registered.
assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME));
@@ -80,8 +110,69 @@
assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE));
- assertNotNull(type.getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE));
+ assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE));
+ assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE));
+ }
- // TODO Write more extensive check -- compare to FallbackAccountType?
+ /**
+ * Initialize with "contacts_fallback.xml" and compare the DataKinds to those of
+ * {@link FallbackAccountType}.
+ */
+ public void testEditSchema_fallback() {
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ getTestContext().getPackageName(), false,
+ getTestContext().getResources().getXml(R.xml.contacts_fallback)
+ );
+
+ assertTrue(type.isInitialized());
+
+ // Create a fallback type with the same resource package name, and compare all the data
+ // kinds to its.
+ final AccountType reference = FallbackAccountType.createForTest(
+ getContext(), type.resPackageName);
+
+ assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds());
+ }
+
+ private static void assertsDataKindEquals(List<DataKind> expectedKinds,
+ List<DataKind> actualKinds) {
+ final int count = Math.max(actualKinds.size(), expectedKinds.size());
+ for (int i = 0; i < count; i++) {
+ String actual = actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)";
+ String expected = expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)";
+
+ // Because assertEquals()'s output is not very friendly when comparing two similar
+ // strings, we manually do the check.
+ if (!Objects.equal(actual, expected)) {
+ final int commonPrefixEnd = findCommonPrefixEnd(actual, expected);
+ fail("Kind #" + i
+ + "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd)
+ + "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd));
+ }
+ }
+ }
+
+ private static int findCommonPrefixEnd(String s1, String s2) {
+ int i = 0;
+ for (;;) {
+ final boolean s1End = (s1.length() <= i);
+ final boolean s2End = (s2.length() <= i);
+ if (s1End || s2End) {
+ return i;
+ }
+ if (s1.charAt(i) != s2.charAt(i)) {
+ return i;
+ }
+ i++;
+ }
+ }
+
+ private static String insertMarkerAt(String s, int position) {
+ final String MARKER = "***";
+ if (position > s.length()) {
+ return s + MARKER;
+ } else {
+ return new StringBuilder(s).insert(position, MARKER).toString();
+ }
}
}