1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.contacts.common.model.account; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.provider.ContactsContract.CommonDataKinds.BaseTypes; 23 import android.provider.ContactsContract.CommonDataKinds.Email; 24 import android.provider.ContactsContract.CommonDataKinds.Event; 25 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 26 import android.provider.ContactsContract.CommonDataKinds.Im; 27 import android.provider.ContactsContract.CommonDataKinds.Nickname; 28 import android.provider.ContactsContract.CommonDataKinds.Note; 29 import android.provider.ContactsContract.CommonDataKinds.Organization; 30 import android.provider.ContactsContract.CommonDataKinds.Phone; 31 import android.provider.ContactsContract.CommonDataKinds.Photo; 32 import android.provider.ContactsContract.CommonDataKinds.Relation; 33 import android.provider.ContactsContract.CommonDataKinds.SipAddress; 34 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 35 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 36 import android.provider.ContactsContract.CommonDataKinds.Website; 37 import android.util.AttributeSet; 38 import android.util.Log; 39 import android.view.inputmethod.EditorInfo; 40 41 import com.android.contacts.common.R; 42 import com.android.contacts.common.model.dataitem.DataKind; 43 import com.android.contacts.common.testing.NeededForTesting; 44 import com.android.contacts.common.util.CommonDateUtils; 45 import com.android.contacts.common.util.ContactDisplayUtils; 46 import com.google.common.collect.Lists; 47 import com.google.common.collect.Maps; 48 49 import org.xmlpull.v1.XmlPullParser; 50 import org.xmlpull.v1.XmlPullParserException; 51 52 import java.io.IOException; 53 import java.util.List; 54 import java.util.Locale; 55 import java.util.Map; 56 57 public abstract class BaseAccountType extends AccountType { 58 private static final String TAG = "BaseAccountType"; 59 60 protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE; 61 protected static final int FLAGS_EMAIL = EditorInfo.TYPE_CLASS_TEXT 62 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; 63 protected static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT 64 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME; 65 protected static final int FLAGS_PHONETIC = EditorInfo.TYPE_CLASS_TEXT 66 | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC; 67 protected static final int FLAGS_GENERIC_NAME = EditorInfo.TYPE_CLASS_TEXT 68 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS; 69 protected static final int FLAGS_NOTE = EditorInfo.TYPE_CLASS_TEXT 70 | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 71 protected static final int FLAGS_EVENT = EditorInfo.TYPE_CLASS_TEXT; 72 protected static final int FLAGS_WEBSITE = EditorInfo.TYPE_CLASS_TEXT 73 | EditorInfo.TYPE_TEXT_VARIATION_URI; 74 protected static final int FLAGS_POSTAL = EditorInfo.TYPE_CLASS_TEXT 75 | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS 76 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 77 protected static final int FLAGS_SIP_ADDRESS = EditorInfo.TYPE_CLASS_TEXT 78 | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; // since SIP addresses have the same 79 // basic format as email addresses 80 protected static final int FLAGS_RELATION = EditorInfo.TYPE_CLASS_TEXT 81 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME; 82 83 // Specify the maximum number of lines that can be used to display various field types. If no 84 // value is specified for a particular type, we use the default value from {@link DataKind}. 85 protected static final int MAX_LINES_FOR_POSTAL_ADDRESS = 10; 86 protected static final int MAX_LINES_FOR_GROUP = 10; 87 protected static final int MAX_LINES_FOR_NOTE = 100; 88 89 private interface Tag { 90 static final String DATA_KIND = "DataKind"; 91 static final String TYPE = "Type"; 92 } 93 94 private interface Attr { 95 static final String MAX_OCCURRENCE = "maxOccurs"; 96 static final String DATE_WITH_TIME = "dateWithTime"; 97 static final String YEAR_OPTIONAL = "yearOptional"; 98 static final String KIND = "kind"; 99 static final String TYPE = "type"; 100 } 101 102 protected interface Weight { 103 static final int NONE = -1; 104 static final int PHONE = 10; 105 static final int EMAIL = 15; 106 static final int STRUCTURED_POSTAL = 25; 107 static final int NICKNAME = 111; 108 static final int EVENT = 120; 109 static final int ORGANIZATION = 125; 110 static final int NOTE = 130; 111 static final int IM = 140; 112 static final int SIP_ADDRESS = 145; 113 static final int GROUP_MEMBERSHIP = 150; 114 static final int WEBSITE = 160; 115 static final int RELATIONSHIP = 999; 116 } 117 BaseAccountType()118 public BaseAccountType() { 119 this.accountType = null; 120 this.dataSet = null; 121 this.titleRes = R.string.account_phone; 122 this.iconRes = R.mipmap.ic_contacts_clr_48cv_44dp; 123 } 124 buildPhoneType(int type)125 protected static EditType buildPhoneType(int type) { 126 return new EditType(type, Phone.getTypeLabelResource(type)); 127 } 128 buildEmailType(int type)129 protected static EditType buildEmailType(int type) { 130 return new EditType(type, Email.getTypeLabelResource(type)); 131 } 132 buildPostalType(int type)133 protected static EditType buildPostalType(int type) { 134 return new EditType(type, StructuredPostal.getTypeLabelResource(type)); 135 } 136 buildImType(int type)137 protected static EditType buildImType(int type) { 138 return new EditType(type, Im.getProtocolLabelResource(type)); 139 } 140 buildEventType(int type, boolean yearOptional)141 protected static EditType buildEventType(int type, boolean yearOptional) { 142 return new EventEditType(type, Event.getTypeResource(type)).setYearOptional(yearOptional); 143 } 144 buildRelationType(int type)145 protected static EditType buildRelationType(int type) { 146 return new EditType(type, Relation.getTypeLabelResource(type)); 147 } 148 addDataKindStructuredName(Context context)149 protected DataKind addDataKindStructuredName(Context context) throws DefinitionException { 150 DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE, 151 R.string.nameLabelsGroup, Weight.NONE, true)); 152 kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup); 153 kind.actionBody = new SimpleInflater(Nickname.NAME); 154 kind.typeOverallMax = 1; 155 156 kind.fieldList = Lists.newArrayList(); 157 kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME, 158 R.string.full_name, FLAGS_PERSON_NAME)); 159 kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 160 FLAGS_PERSON_NAME).setLongForm(true)); 161 kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 162 FLAGS_PERSON_NAME).setLongForm(true)); 163 kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 164 FLAGS_PERSON_NAME).setLongForm(true)); 165 kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 166 FLAGS_PERSON_NAME).setLongForm(true)); 167 kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 168 FLAGS_PERSON_NAME).setLongForm(true)); 169 kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME, 170 R.string.name_phonetic_family, FLAGS_PHONETIC)); 171 kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME, 172 R.string.name_phonetic_middle, FLAGS_PHONETIC)); 173 kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME, 174 R.string.name_phonetic_given, FLAGS_PHONETIC)); 175 176 return kind; 177 } 178 addDataKindDisplayName(Context context)179 protected DataKind addDataKindDisplayName(Context context) throws DefinitionException { 180 DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME, 181 R.string.nameLabelsGroup, Weight.NONE, true)); 182 kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup); 183 kind.actionBody = new SimpleInflater(Nickname.NAME); 184 kind.typeOverallMax = 1; 185 186 kind.fieldList = Lists.newArrayList(); 187 kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME, 188 R.string.full_name, FLAGS_PERSON_NAME).setShortForm(true)); 189 190 boolean displayOrderPrimary = 191 context.getResources().getBoolean(R.bool.config_editor_field_order_primary); 192 193 if (!displayOrderPrimary) { 194 kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 195 FLAGS_PERSON_NAME).setLongForm(true)); 196 kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 197 FLAGS_PERSON_NAME).setLongForm(true)); 198 kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 199 FLAGS_PERSON_NAME).setLongForm(true)); 200 kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 201 FLAGS_PERSON_NAME).setLongForm(true)); 202 kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 203 FLAGS_PERSON_NAME).setLongForm(true)); 204 } else { 205 kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 206 FLAGS_PERSON_NAME).setLongForm(true)); 207 kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 208 FLAGS_PERSON_NAME).setLongForm(true)); 209 kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 210 FLAGS_PERSON_NAME).setLongForm(true)); 211 kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 212 FLAGS_PERSON_NAME).setLongForm(true)); 213 kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 214 FLAGS_PERSON_NAME).setLongForm(true)); 215 } 216 217 return kind; 218 } 219 addDataKindPhoneticName(Context context)220 protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException { 221 DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME, 222 R.string.name_phonetic, Weight.NONE, true)); 223 kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup); 224 kind.actionBody = new SimpleInflater(Nickname.NAME); 225 kind.typeOverallMax = 1; 226 227 kind.fieldList = Lists.newArrayList(); 228 kind.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME, 229 R.string.name_phonetic, FLAGS_PHONETIC).setShortForm(true)); 230 kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME, 231 R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true)); 232 kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME, 233 R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true)); 234 kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME, 235 R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true)); 236 237 return kind; 238 } 239 addDataKindNickname(Context context)240 protected DataKind addDataKindNickname(Context context) throws DefinitionException { 241 DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE, 242 R.string.nicknameLabelsGroup, Weight.NICKNAME, true)); 243 kind.typeOverallMax = 1; 244 kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup); 245 kind.actionBody = new SimpleInflater(Nickname.NAME); 246 kind.defaultValues = new ContentValues(); 247 kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT); 248 249 kind.fieldList = Lists.newArrayList(); 250 kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup, 251 FLAGS_PERSON_NAME)); 252 253 return kind; 254 } 255 addDataKindPhone(Context context)256 protected DataKind addDataKindPhone(Context context) throws DefinitionException { 257 DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup, 258 Weight.PHONE, true)); 259 kind.iconAltRes = R.drawable.ic_text_holo_light; 260 kind.iconAltDescriptionRes = R.string.sms; 261 kind.actionHeader = new PhoneActionInflater(); 262 kind.actionAltHeader = new PhoneActionAltInflater(); 263 kind.actionBody = new SimpleInflater(Phone.NUMBER); 264 kind.typeColumn = Phone.TYPE; 265 kind.typeList = Lists.newArrayList(); 266 kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE)); 267 kind.typeList.add(buildPhoneType(Phone.TYPE_HOME)); 268 kind.typeList.add(buildPhoneType(Phone.TYPE_WORK)); 269 kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true)); 270 kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true)); 271 kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true)); 272 kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER)); 273 kind.typeList.add( 274 buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Phone.LABEL)); 275 kind.typeList.add(buildPhoneType(Phone.TYPE_CALLBACK).setSecondary(true)); 276 kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true)); 277 kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true)); 278 kind.typeList.add(buildPhoneType(Phone.TYPE_ISDN).setSecondary(true)); 279 kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN).setSecondary(true)); 280 kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER_FAX).setSecondary(true)); 281 kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true)); 282 kind.typeList.add(buildPhoneType(Phone.TYPE_TELEX).setSecondary(true)); 283 kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true)); 284 kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true)); 285 kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true)); 286 kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true)); 287 kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true)); 288 289 kind.fieldList = Lists.newArrayList(); 290 kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE)); 291 292 return kind; 293 } 294 addDataKindEmail(Context context)295 protected DataKind addDataKindEmail(Context context) throws DefinitionException { 296 DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup, 297 Weight.EMAIL, true)); 298 kind.actionHeader = new EmailActionInflater(); 299 kind.actionBody = new SimpleInflater(Email.DATA); 300 kind.typeColumn = Email.TYPE; 301 kind.typeList = Lists.newArrayList(); 302 kind.typeList.add(buildEmailType(Email.TYPE_HOME)); 303 kind.typeList.add(buildEmailType(Email.TYPE_WORK)); 304 kind.typeList.add(buildEmailType(Email.TYPE_OTHER)); 305 kind.typeList.add(buildEmailType(Email.TYPE_MOBILE)); 306 kind.typeList.add( 307 buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL)); 308 309 kind.fieldList = Lists.newArrayList(); 310 kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL)); 311 312 return kind; 313 } 314 addDataKindStructuredPostal(Context context)315 protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException { 316 DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE, 317 R.string.postalLabelsGroup, Weight.STRUCTURED_POSTAL, true)); 318 kind.actionHeader = new PostalActionInflater(); 319 kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS); 320 kind.typeColumn = StructuredPostal.TYPE; 321 kind.typeList = Lists.newArrayList(); 322 kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME)); 323 kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK)); 324 kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER)); 325 kind.typeList.add(buildPostalType(StructuredPostal.TYPE_CUSTOM).setSecondary(true) 326 .setCustomColumn(StructuredPostal.LABEL)); 327 328 kind.fieldList = Lists.newArrayList(); 329 kind.fieldList.add( 330 new EditField(StructuredPostal.FORMATTED_ADDRESS, R.string.postal_address, 331 FLAGS_POSTAL)); 332 333 kind.maxLinesForDisplay = MAX_LINES_FOR_POSTAL_ADDRESS; 334 335 return kind; 336 } 337 addDataKindIm(Context context)338 protected DataKind addDataKindIm(Context context) throws DefinitionException { 339 DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, 340 Weight.IM, true)); 341 kind.actionHeader = new ImActionInflater(); 342 kind.actionBody = new SimpleInflater(Im.DATA); 343 344 // NOTE: even though a traditional "type" exists, for editing 345 // purposes we're using the protocol to pick labels 346 347 kind.defaultValues = new ContentValues(); 348 kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER); 349 350 kind.typeColumn = Im.PROTOCOL; 351 kind.typeList = Lists.newArrayList(); 352 kind.typeList.add(buildImType(Im.PROTOCOL_AIM)); 353 kind.typeList.add(buildImType(Im.PROTOCOL_MSN)); 354 kind.typeList.add(buildImType(Im.PROTOCOL_YAHOO)); 355 kind.typeList.add(buildImType(Im.PROTOCOL_SKYPE)); 356 kind.typeList.add(buildImType(Im.PROTOCOL_QQ)); 357 kind.typeList.add(buildImType(Im.PROTOCOL_GOOGLE_TALK)); 358 kind.typeList.add(buildImType(Im.PROTOCOL_ICQ)); 359 kind.typeList.add(buildImType(Im.PROTOCOL_JABBER)); 360 kind.typeList.add(buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true).setCustomColumn( 361 Im.CUSTOM_PROTOCOL)); 362 363 kind.fieldList = Lists.newArrayList(); 364 kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL)); 365 366 return kind; 367 } 368 addDataKindOrganization(Context context)369 protected DataKind addDataKindOrganization(Context context) throws DefinitionException { 370 DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE, 371 R.string.organizationLabelsGroup, Weight.ORGANIZATION, true)); 372 kind.actionHeader = new SimpleInflater(R.string.organizationLabelsGroup); 373 kind.actionBody = ORGANIZATION_BODY_INFLATER; 374 kind.typeOverallMax = 1; 375 376 kind.fieldList = Lists.newArrayList(); 377 kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company, 378 FLAGS_GENERIC_NAME)); 379 kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title, 380 FLAGS_GENERIC_NAME)); 381 382 return kind; 383 } 384 addDataKindPhoto(Context context)385 protected DataKind addDataKindPhoto(Context context) throws DefinitionException { 386 DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, Weight.NONE, true)); 387 kind.typeOverallMax = 1; 388 kind.fieldList = Lists.newArrayList(); 389 kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1)); 390 return kind; 391 } 392 addDataKindNote(Context context)393 protected DataKind addDataKindNote(Context context) throws DefinitionException { 394 DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE, R.string.label_notes, 395 Weight.NOTE, true)); 396 kind.typeOverallMax = 1; 397 kind.actionHeader = new SimpleInflater(R.string.label_notes); 398 kind.actionBody = new SimpleInflater(Note.NOTE); 399 kind.fieldList = Lists.newArrayList(); 400 kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE)); 401 402 kind.maxLinesForDisplay = MAX_LINES_FOR_NOTE; 403 404 return kind; 405 } 406 addDataKindWebsite(Context context)407 protected DataKind addDataKindWebsite(Context context) throws DefinitionException { 408 DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE, 409 R.string.websiteLabelsGroup, Weight.WEBSITE, true)); 410 kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup); 411 kind.actionBody = new SimpleInflater(Website.URL); 412 kind.defaultValues = new ContentValues(); 413 kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER); 414 415 kind.fieldList = Lists.newArrayList(); 416 kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE)); 417 418 return kind; 419 } 420 addDataKindSipAddress(Context context)421 protected DataKind addDataKindSipAddress(Context context) throws DefinitionException { 422 DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE, 423 R.string.label_sip_address, Weight.SIP_ADDRESS, true)); 424 425 kind.typeOverallMax = 1; 426 kind.actionHeader = new SimpleInflater(R.string.label_sip_address); 427 kind.actionBody = new SimpleInflater(SipAddress.SIP_ADDRESS); 428 kind.fieldList = Lists.newArrayList(); 429 kind.fieldList.add(new EditField(SipAddress.SIP_ADDRESS, 430 R.string.label_sip_address, FLAGS_SIP_ADDRESS)); 431 432 return kind; 433 } 434 addDataKindGroupMembership(Context context)435 protected DataKind addDataKindGroupMembership(Context context) throws DefinitionException { 436 DataKind kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE, 437 R.string.groupsLabel, Weight.GROUP_MEMBERSHIP, true)); 438 439 kind.typeOverallMax = 1; 440 kind.fieldList = Lists.newArrayList(); 441 kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1)); 442 443 kind.maxLinesForDisplay = MAX_LINES_FOR_GROUP; 444 445 return kind; 446 } 447 448 /** 449 * Simple inflater that assumes a string resource has a "%s" that will be 450 * filled from the given column. 451 */ 452 public static class SimpleInflater implements StringInflater { 453 private final int mStringRes; 454 private final String mColumnName; 455 SimpleInflater(int stringRes)456 public SimpleInflater(int stringRes) { 457 this(stringRes, null); 458 } 459 SimpleInflater(String columnName)460 public SimpleInflater(String columnName) { 461 this(-1, columnName); 462 } 463 SimpleInflater(int stringRes, String columnName)464 public SimpleInflater(int stringRes, String columnName) { 465 mStringRes = stringRes; 466 mColumnName = columnName; 467 } 468 469 @Override inflateUsing(Context context, ContentValues values)470 public CharSequence inflateUsing(Context context, ContentValues values) { 471 final boolean validColumn = values.containsKey(mColumnName); 472 final boolean validString = mStringRes > 0; 473 474 final CharSequence stringValue = validString ? context.getText(mStringRes) : null; 475 final CharSequence columnValue = validColumn ? values.getAsString(mColumnName) : null; 476 477 if (validString && validColumn) { 478 return String.format(stringValue.toString(), columnValue); 479 } else if (validString) { 480 return stringValue; 481 } else if (validColumn) { 482 return columnValue; 483 } else { 484 return null; 485 } 486 } 487 488 @Override toString()489 public String toString() { 490 return this.getClass().getSimpleName() 491 + " mStringRes=" + mStringRes 492 + " mColumnName" + mColumnName; 493 } 494 495 @NeededForTesting getColumnNameForTest()496 public String getColumnNameForTest() { 497 return mColumnName; 498 } 499 } 500 501 public static abstract class CommonInflater implements StringInflater { getTypeLabelResource(Integer type)502 protected abstract int getTypeLabelResource(Integer type); 503 isCustom(Integer type)504 protected boolean isCustom(Integer type) { 505 return type == BaseTypes.TYPE_CUSTOM; 506 } 507 getTypeColumn()508 protected String getTypeColumn() { 509 return Phone.TYPE; 510 } 511 getLabelColumn()512 protected String getLabelColumn() { 513 return Phone.LABEL; 514 } 515 getTypeLabel(Resources res, Integer type, CharSequence label)516 protected CharSequence getTypeLabel(Resources res, Integer type, CharSequence label) { 517 final int labelRes = getTypeLabelResource(type); 518 if (type == null) { 519 return res.getText(labelRes); 520 } else if (isCustom(type)) { 521 return res.getString(labelRes, label == null ? "" : label); 522 } else { 523 return res.getText(labelRes); 524 } 525 } 526 527 @Override inflateUsing(Context context, ContentValues values)528 public CharSequence inflateUsing(Context context, ContentValues values) { 529 final Integer type = values.getAsInteger(getTypeColumn()); 530 final String label = values.getAsString(getLabelColumn()); 531 return getTypeLabel(context.getResources(), type, label); 532 } 533 534 @Override toString()535 public String toString() { 536 return this.getClass().getSimpleName(); 537 } 538 } 539 540 public static class PhoneActionInflater extends CommonInflater { 541 @Override isCustom(Integer type)542 protected boolean isCustom(Integer type) { 543 return ContactDisplayUtils.isCustomPhoneType(type); 544 } 545 546 @Override getTypeLabelResource(Integer type)547 protected int getTypeLabelResource(Integer type) { 548 return ContactDisplayUtils.getPhoneLabelResourceId(type); 549 } 550 } 551 552 public static class PhoneActionAltInflater extends CommonInflater { 553 @Override isCustom(Integer type)554 protected boolean isCustom(Integer type) { 555 return ContactDisplayUtils.isCustomPhoneType(type); 556 } 557 558 @Override getTypeLabelResource(Integer type)559 protected int getTypeLabelResource(Integer type) { 560 return ContactDisplayUtils.getSmsLabelResourceId(type); 561 } 562 } 563 564 public static class EmailActionInflater extends CommonInflater { 565 @Override getTypeLabelResource(Integer type)566 protected int getTypeLabelResource(Integer type) { 567 if (type == null) return R.string.email; 568 switch (type) { 569 case Email.TYPE_HOME: return R.string.email_home; 570 case Email.TYPE_WORK: return R.string.email_work; 571 case Email.TYPE_OTHER: return R.string.email_other; 572 case Email.TYPE_MOBILE: return R.string.email_mobile; 573 default: return R.string.email_custom; 574 } 575 } 576 } 577 578 public static class EventActionInflater extends CommonInflater { 579 @Override getTypeLabelResource(Integer type)580 protected int getTypeLabelResource(Integer type) { 581 return Event.getTypeResource(type); 582 } 583 } 584 585 public static class RelationActionInflater extends CommonInflater { 586 @Override getTypeLabelResource(Integer type)587 protected int getTypeLabelResource(Integer type) { 588 return Relation.getTypeLabelResource(type == null ? Relation.TYPE_CUSTOM : type); 589 } 590 } 591 592 public static class PostalActionInflater extends CommonInflater { 593 @Override getTypeLabelResource(Integer type)594 protected int getTypeLabelResource(Integer type) { 595 if (type == null) return R.string.map_other; 596 switch (type) { 597 case StructuredPostal.TYPE_HOME: return R.string.map_home; 598 case StructuredPostal.TYPE_WORK: return R.string.map_work; 599 case StructuredPostal.TYPE_OTHER: return R.string.map_other; 600 default: return R.string.map_custom; 601 } 602 } 603 } 604 605 public static class ImActionInflater extends CommonInflater { 606 @Override getTypeColumn()607 protected String getTypeColumn() { 608 return Im.PROTOCOL; 609 } 610 611 @Override getLabelColumn()612 protected String getLabelColumn() { 613 return Im.CUSTOM_PROTOCOL; 614 } 615 616 @Override getTypeLabelResource(Integer type)617 protected int getTypeLabelResource(Integer type) { 618 if (type == null) return R.string.chat; 619 switch (type) { 620 case Im.PROTOCOL_AIM: return R.string.chat_aim; 621 case Im.PROTOCOL_MSN: return R.string.chat_msn; 622 case Im.PROTOCOL_YAHOO: return R.string.chat_yahoo; 623 case Im.PROTOCOL_SKYPE: return R.string.chat_skype; 624 case Im.PROTOCOL_QQ: return R.string.chat_qq; 625 case Im.PROTOCOL_GOOGLE_TALK: return R.string.chat_gtalk; 626 case Im.PROTOCOL_ICQ: return R.string.chat_icq; 627 case Im.PROTOCOL_JABBER: return R.string.chat_jabber; 628 case Im.PROTOCOL_NETMEETING: return R.string.chat; 629 default: return R.string.chat; 630 } 631 } 632 } 633 634 public static final StringInflater ORGANIZATION_BODY_INFLATER = new StringInflater() { 635 @Override 636 public CharSequence inflateUsing(Context context, ContentValues values) { 637 final CharSequence companyValue = values.containsKey(Organization.COMPANY) ? 638 values.getAsString(Organization.COMPANY) : null; 639 final CharSequence titleValue = values.containsKey(Organization.TITLE) ? 640 values.getAsString(Organization.TITLE) : null; 641 642 if (companyValue != null && titleValue != null) { 643 return companyValue + ": " + titleValue; 644 } else if (companyValue == null) { 645 return titleValue; 646 } else { 647 return companyValue; 648 } 649 } 650 }; 651 652 @Override isGroupMembershipEditable()653 public boolean isGroupMembershipEditable() { 654 return false; 655 } 656 657 /** 658 * Parses the content of the EditSchema tag in contacts.xml. 659 */ parseEditSchema(Context context, XmlPullParser parser, AttributeSet attrs)660 protected final void parseEditSchema(Context context, XmlPullParser parser, AttributeSet attrs) 661 throws XmlPullParserException, IOException, DefinitionException { 662 663 final int outerDepth = parser.getDepth(); 664 int type; 665 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 666 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 667 final int depth = parser.getDepth(); 668 if (type != XmlPullParser.START_TAG || depth != outerDepth + 1) { 669 continue; // Not direct child tag 670 } 671 672 final String tag = parser.getName(); 673 674 if (Tag.DATA_KIND.equals(tag)) { 675 for (DataKind kind : KindParser.INSTANCE.parseDataKindTag(context, parser, attrs)) { 676 addKind(kind); 677 } 678 } else { 679 Log.w(TAG, "Skipping unknown tag " + tag); 680 } 681 } 682 } 683 684 // Utility methods to keep code shorter. getAttr(AttributeSet attrs, String attribute, boolean defaultValue)685 private static boolean getAttr(AttributeSet attrs, String attribute, boolean defaultValue) { 686 return attrs.getAttributeBooleanValue(null, attribute, defaultValue); 687 } 688 getAttr(AttributeSet attrs, String attribute, int defaultValue)689 private static int getAttr(AttributeSet attrs, String attribute, int defaultValue) { 690 return attrs.getAttributeIntValue(null, attribute, defaultValue); 691 } 692 getAttr(AttributeSet attrs, String attribute)693 private static String getAttr(AttributeSet attrs, String attribute) { 694 return attrs.getAttributeValue(null, attribute); 695 } 696 697 // TODO Extract it to its own class, and move all KindBuilders to it as well. 698 private static class KindParser { 699 public static final KindParser INSTANCE = new KindParser(); 700 701 private final Map<String, KindBuilder> mBuilders = Maps.newHashMap(); 702 KindParser()703 private KindParser() { 704 addBuilder(new NameKindBuilder()); 705 addBuilder(new NicknameKindBuilder()); 706 addBuilder(new PhoneKindBuilder()); 707 addBuilder(new EmailKindBuilder()); 708 addBuilder(new StructuredPostalKindBuilder()); 709 addBuilder(new ImKindBuilder()); 710 addBuilder(new OrganizationKindBuilder()); 711 addBuilder(new PhotoKindBuilder()); 712 addBuilder(new NoteKindBuilder()); 713 addBuilder(new WebsiteKindBuilder()); 714 addBuilder(new SipAddressKindBuilder()); 715 addBuilder(new GroupMembershipKindBuilder()); 716 addBuilder(new EventKindBuilder()); 717 addBuilder(new RelationshipKindBuilder()); 718 } 719 addBuilder(KindBuilder builder)720 private void addBuilder(KindBuilder builder) { 721 mBuilders.put(builder.getTagName(), builder); 722 } 723 724 /** 725 * Takes a {@link XmlPullParser} at the start of a DataKind tag, parses it and returns 726 * {@link DataKind}s. (Usually just one, but there are three for the "name" kind.) 727 * 728 * This method returns a list, because we need to add 3 kinds for the name data kind. 729 * (structured, display and phonetic) 730 */ parseDataKindTag(Context context, XmlPullParser parser, AttributeSet attrs)731 public List<DataKind> parseDataKindTag(Context context, XmlPullParser parser, 732 AttributeSet attrs) 733 throws DefinitionException, XmlPullParserException, IOException { 734 final String kind = getAttr(attrs, Attr.KIND); 735 final KindBuilder builder = mBuilders.get(kind); 736 if (builder != null) { 737 return builder.parseDataKind(context, parser, attrs); 738 } else { 739 throw new DefinitionException("Undefined data kind '" + kind + "'"); 740 } 741 } 742 } 743 744 private static abstract class KindBuilder { 745 getTagName()746 public abstract String getTagName(); 747 748 /** 749 * DataKind tag parser specific to each kind. Subclasses must implement it. 750 */ parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)751 public abstract List<DataKind> parseDataKind(Context context, XmlPullParser parser, 752 AttributeSet attrs) throws DefinitionException, XmlPullParserException, IOException; 753 754 /** 755 * Creates a new {@link DataKind}, and also parses the child Type tags in the DataKind 756 * tag. 757 */ newDataKind(Context context, XmlPullParser parser, AttributeSet attrs, boolean isPseudo, String mimeType, String typeColumn, int titleRes, int weight, StringInflater actionHeader, StringInflater actionBody)758 protected final DataKind newDataKind(Context context, XmlPullParser parser, 759 AttributeSet attrs, boolean isPseudo, String mimeType, String typeColumn, 760 int titleRes, int weight, StringInflater actionHeader, StringInflater actionBody) 761 throws DefinitionException, XmlPullParserException, IOException { 762 763 if (Log.isLoggable(TAG, Log.DEBUG)) { 764 Log.d(TAG, "Adding DataKind: " + mimeType); 765 } 766 767 final DataKind kind = new DataKind(mimeType, titleRes, weight, true); 768 kind.typeColumn = typeColumn; 769 kind.actionHeader = actionHeader; 770 kind.actionBody = actionBody; 771 kind.fieldList = Lists.newArrayList(); 772 773 // Get more information from the tag... 774 // A pseudo data kind doesn't have corresponding tag the XML, so we skip this. 775 if (!isPseudo) { 776 kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1); 777 778 // Process "Type" tags. 779 // If a kind has the type column, contacts.xml must have at least one type 780 // definition. Otherwise, it mustn't have a type definition. 781 if (kind.typeColumn != null) { 782 // Parse and add types. 783 kind.typeList = Lists.newArrayList(); 784 parseTypes(context, parser, attrs, kind, true); 785 if (kind.typeList.size() == 0) { 786 throw new DefinitionException( 787 "Kind " + kind.mimeType + " must have at least one type"); 788 } 789 } else { 790 // Make sure it has no types. 791 parseTypes(context, parser, attrs, kind, false /* can't have types */); 792 } 793 } 794 795 return kind; 796 } 797 798 /** 799 * Parses Type elements in a DataKind element, and if {@code canHaveTypes} is true adds 800 * them to the given {@link DataKind}. Otherwise the {@link DataKind} can't have a type, 801 * so throws {@link DefinitionException}. 802 */ parseTypes(Context context, XmlPullParser parser, AttributeSet attrs, DataKind kind, boolean canHaveTypes)803 private void parseTypes(Context context, XmlPullParser parser, AttributeSet attrs, 804 DataKind kind, boolean canHaveTypes) 805 throws DefinitionException, XmlPullParserException, IOException { 806 final int outerDepth = parser.getDepth(); 807 int type; 808 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 809 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 810 final int depth = parser.getDepth(); 811 if (type != XmlPullParser.START_TAG || depth != outerDepth + 1) { 812 continue; // Not direct child tag 813 } 814 815 final String tag = parser.getName(); 816 if (Tag.TYPE.equals(tag)) { 817 if (canHaveTypes) { 818 kind.typeList.add(parseTypeTag(parser, attrs, kind)); 819 } else { 820 throw new DefinitionException( 821 "Kind " + kind.mimeType + " can't have types"); 822 } 823 } else { 824 throw new DefinitionException("Unknown tag: " + tag); 825 } 826 } 827 } 828 829 /** 830 * Parses a single Type element and returns an {@link EditType} built from it. Uses 831 * {@link #buildEditTypeForTypeTag} defined in subclasses to actually build an 832 * {@link EditType}. 833 */ parseTypeTag(XmlPullParser parser, AttributeSet attrs, DataKind kind)834 private EditType parseTypeTag(XmlPullParser parser, AttributeSet attrs, DataKind kind) 835 throws DefinitionException { 836 837 final String typeName = getAttr(attrs, Attr.TYPE); 838 839 final EditType et = buildEditTypeForTypeTag(attrs, typeName); 840 if (et == null) { 841 throw new DefinitionException( 842 "Undefined type '" + typeName + "' for data kind '" + kind.mimeType + "'"); 843 } 844 et.specificMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1); 845 846 return et; 847 } 848 849 /** 850 * Returns an {@link EditType} for the given "type". Subclasses may optionally use 851 * the attributes in the tag to set optional values. 852 * (e.g. "yearOptional" for the event kind) 853 */ buildEditTypeForTypeTag(AttributeSet attrs, String type)854 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 855 return null; 856 } 857 throwIfList(DataKind kind)858 protected final void throwIfList(DataKind kind) throws DefinitionException { 859 if (kind.typeOverallMax != 1) { 860 throw new DefinitionException( 861 "Kind " + kind.mimeType + " must have 'overallMax=\"1\"'"); 862 } 863 } 864 } 865 866 /** 867 * DataKind parser for Name. (structured, display, phonetic) 868 */ 869 private static class NameKindBuilder extends KindBuilder { 870 @Override getTagName()871 public String getTagName() { 872 return "name"; 873 } 874 checkAttributeTrue(boolean value, String attrName)875 private static void checkAttributeTrue(boolean value, String attrName) 876 throws DefinitionException { 877 if (!value) { 878 throw new DefinitionException(attrName + " must be true"); 879 } 880 } 881 882 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)883 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 884 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 885 IOException { 886 887 // Build 3 data kinds: 888 // - StructuredName.CONTENT_ITEM_TYPE 889 // - DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME 890 // - DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME 891 892 final boolean displayOrderPrimary = 893 context.getResources().getBoolean(R.bool.config_editor_field_order_primary); 894 895 final boolean supportsDisplayName = getAttr(attrs, "supportsDisplayName", false); 896 final boolean supportsPrefix = getAttr(attrs, "supportsPrefix", false); 897 final boolean supportsMiddleName = getAttr(attrs, "supportsMiddleName", false); 898 final boolean supportsSuffix = getAttr(attrs, "supportsSuffix", false); 899 final boolean supportsPhoneticFamilyName = 900 getAttr(attrs, "supportsPhoneticFamilyName", false); 901 final boolean supportsPhoneticMiddleName = 902 getAttr(attrs, "supportsPhoneticMiddleName", false); 903 final boolean supportsPhoneticGivenName = 904 getAttr(attrs, "supportsPhoneticGivenName", false); 905 906 // For now, every things must be supported. 907 checkAttributeTrue(supportsDisplayName, "supportsDisplayName"); 908 checkAttributeTrue(supportsPrefix, "supportsPrefix"); 909 checkAttributeTrue(supportsMiddleName, "supportsMiddleName"); 910 checkAttributeTrue(supportsSuffix, "supportsSuffix"); 911 checkAttributeTrue(supportsPhoneticFamilyName, "supportsPhoneticFamilyName"); 912 checkAttributeTrue(supportsPhoneticMiddleName, "supportsPhoneticMiddleName"); 913 checkAttributeTrue(supportsPhoneticGivenName, "supportsPhoneticGivenName"); 914 915 final List<DataKind> kinds = Lists.newArrayList(); 916 917 // Structured name 918 final DataKind ks = newDataKind(context, parser, attrs, false, 919 StructuredName.CONTENT_ITEM_TYPE, null, R.string.nameLabelsGroup, Weight.NONE, 920 new SimpleInflater(R.string.nameLabelsGroup), 921 new SimpleInflater(Nickname.NAME)); 922 923 throwIfList(ks); 924 kinds.add(ks); 925 926 // Note about setLongForm/setShortForm below. 927 // We need to set this only when the type supports display name. (=supportsDisplayName) 928 // Otherwise (i.e. Exchange) we don't set these flags, but instead make some fields 929 // "optional". 930 931 ks.fieldList.add(new EditField(StructuredName.DISPLAY_NAME, R.string.full_name, 932 FLAGS_PERSON_NAME)); 933 ks.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 934 FLAGS_PERSON_NAME).setLongForm(true)); 935 ks.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 936 FLAGS_PERSON_NAME).setLongForm(true)); 937 ks.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 938 FLAGS_PERSON_NAME).setLongForm(true)); 939 ks.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 940 FLAGS_PERSON_NAME).setLongForm(true)); 941 ks.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 942 FLAGS_PERSON_NAME).setLongForm(true)); 943 ks.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME, 944 R.string.name_phonetic_family, FLAGS_PHONETIC)); 945 ks.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME, 946 R.string.name_phonetic_middle, FLAGS_PHONETIC)); 947 ks.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME, 948 R.string.name_phonetic_given, FLAGS_PHONETIC)); 949 950 // Display name 951 final DataKind kd = newDataKind(context, parser, attrs, true, 952 DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME, null, 953 R.string.nameLabelsGroup, Weight.NONE, 954 new SimpleInflater(R.string.nameLabelsGroup), 955 new SimpleInflater(Nickname.NAME)); 956 kd.typeOverallMax = 1; 957 kinds.add(kd); 958 959 kd.fieldList.add(new EditField(StructuredName.DISPLAY_NAME, 960 R.string.full_name, FLAGS_PERSON_NAME).setShortForm(true)); 961 962 if (!displayOrderPrimary) { 963 kd.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 964 FLAGS_PERSON_NAME).setLongForm(true)); 965 kd.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 966 FLAGS_PERSON_NAME).setLongForm(true)); 967 kd.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 968 FLAGS_PERSON_NAME).setLongForm(true)); 969 kd.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 970 FLAGS_PERSON_NAME).setLongForm(true)); 971 kd.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 972 FLAGS_PERSON_NAME).setLongForm(true)); 973 } else { 974 kd.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix, 975 FLAGS_PERSON_NAME).setLongForm(true)); 976 kd.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given, 977 FLAGS_PERSON_NAME).setLongForm(true)); 978 kd.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle, 979 FLAGS_PERSON_NAME).setLongForm(true)); 980 kd.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family, 981 FLAGS_PERSON_NAME).setLongForm(true)); 982 kd.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix, 983 FLAGS_PERSON_NAME).setLongForm(true)); 984 } 985 986 // Phonetic name 987 final DataKind kp = newDataKind(context, parser, attrs, true, 988 DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME, null, 989 R.string.name_phonetic, Weight.NONE, 990 new SimpleInflater(R.string.nameLabelsGroup), 991 new SimpleInflater(Nickname.NAME)); 992 kp.typeOverallMax = 1; 993 kinds.add(kp); 994 995 // We may want to change the order depending on displayOrderPrimary too. 996 kp.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME, 997 R.string.name_phonetic, FLAGS_PHONETIC).setShortForm(true)); 998 kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME, 999 R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true)); 1000 kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME, 1001 R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true)); 1002 kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME, 1003 R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true)); 1004 return kinds; 1005 } 1006 } 1007 1008 private static class NicknameKindBuilder extends KindBuilder { 1009 @Override getTagName()1010 public String getTagName() { 1011 return "nickname"; 1012 } 1013 1014 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1015 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1016 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1017 IOException { 1018 final DataKind kind = newDataKind(context, parser, attrs, false, 1019 Nickname.CONTENT_ITEM_TYPE, null, R.string.nicknameLabelsGroup, Weight.NICKNAME, 1020 new SimpleInflater(R.string.nicknameLabelsGroup), 1021 new SimpleInflater(Nickname.NAME)); 1022 1023 kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup, 1024 FLAGS_PERSON_NAME)); 1025 1026 kind.defaultValues = new ContentValues(); 1027 kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT); 1028 1029 throwIfList(kind); 1030 return Lists.newArrayList(kind); 1031 } 1032 } 1033 1034 private static class PhoneKindBuilder extends KindBuilder { 1035 @Override getTagName()1036 public String getTagName() { 1037 return "phone"; 1038 } 1039 1040 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1041 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1042 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1043 IOException { 1044 final DataKind kind = newDataKind(context, parser, attrs, false, 1045 Phone.CONTENT_ITEM_TYPE, Phone.TYPE, R.string.phoneLabelsGroup, Weight.PHONE, 1046 new PhoneActionInflater(), new SimpleInflater(Phone.NUMBER)); 1047 1048 kind.iconAltRes = R.drawable.ic_text_holo_light; 1049 kind.iconAltDescriptionRes = R.string.sms; 1050 kind.actionAltHeader = new PhoneActionAltInflater(); 1051 1052 kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE)); 1053 1054 return Lists.newArrayList(kind); 1055 } 1056 1057 /** Just to avoid line-wrapping... */ build(int type, boolean secondary)1058 protected static EditType build(int type, boolean secondary) { 1059 return new EditType(type, Phone.getTypeLabelResource(type)).setSecondary(secondary); 1060 } 1061 1062 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1063 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1064 if ("home".equals(type)) return build(Phone.TYPE_HOME, false); 1065 if ("mobile".equals(type)) return build(Phone.TYPE_MOBILE, false); 1066 if ("work".equals(type)) return build(Phone.TYPE_WORK, false); 1067 if ("fax_work".equals(type)) return build(Phone.TYPE_FAX_WORK, true); 1068 if ("fax_home".equals(type)) return build(Phone.TYPE_FAX_HOME, true); 1069 if ("pager".equals(type)) return build(Phone.TYPE_PAGER, true); 1070 if ("other".equals(type)) return build(Phone.TYPE_OTHER, false); 1071 if ("callback".equals(type)) return build(Phone.TYPE_CALLBACK, true); 1072 if ("car".equals(type)) return build(Phone.TYPE_CAR, true); 1073 if ("company_main".equals(type)) return build(Phone.TYPE_COMPANY_MAIN, true); 1074 if ("isdn".equals(type)) return build(Phone.TYPE_ISDN, true); 1075 if ("main".equals(type)) return build(Phone.TYPE_MAIN, true); 1076 if ("other_fax".equals(type)) return build(Phone.TYPE_OTHER_FAX, true); 1077 if ("radio".equals(type)) return build(Phone.TYPE_RADIO, true); 1078 if ("telex".equals(type)) return build(Phone.TYPE_TELEX, true); 1079 if ("tty_tdd".equals(type)) return build(Phone.TYPE_TTY_TDD, true); 1080 if ("work_mobile".equals(type)) return build(Phone.TYPE_WORK_MOBILE, true); 1081 if ("work_pager".equals(type)) return build(Phone.TYPE_WORK_PAGER, true); 1082 1083 // Note "assistant" used to be a custom column for the fallback type, but not anymore. 1084 if ("assistant".equals(type)) return build(Phone.TYPE_ASSISTANT, true); 1085 if ("mms".equals(type)) return build(Phone.TYPE_MMS, true); 1086 if ("custom".equals(type)) { 1087 return build(Phone.TYPE_CUSTOM, true).setCustomColumn(Phone.LABEL); 1088 } 1089 return null; 1090 } 1091 } 1092 1093 private static class EmailKindBuilder extends KindBuilder { 1094 @Override getTagName()1095 public String getTagName() { 1096 return "email"; 1097 } 1098 1099 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1100 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1101 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1102 IOException { 1103 final DataKind kind = newDataKind(context, parser, attrs, false, 1104 Email.CONTENT_ITEM_TYPE, Email.TYPE, R.string.emailLabelsGroup, Weight.EMAIL, 1105 new EmailActionInflater(), new SimpleInflater(Email.DATA)); 1106 kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL)); 1107 1108 return Lists.newArrayList(kind); 1109 } 1110 1111 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1112 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1113 // EditType is mutable, so we need to create a new instance every time. 1114 if ("home".equals(type)) return buildEmailType(Email.TYPE_HOME); 1115 if ("work".equals(type)) return buildEmailType(Email.TYPE_WORK); 1116 if ("other".equals(type)) return buildEmailType(Email.TYPE_OTHER); 1117 if ("mobile".equals(type)) return buildEmailType(Email.TYPE_MOBILE); 1118 if ("custom".equals(type)) { 1119 return buildEmailType(Email.TYPE_CUSTOM) 1120 .setSecondary(true).setCustomColumn(Email.LABEL); 1121 } 1122 return null; 1123 } 1124 } 1125 1126 private static class StructuredPostalKindBuilder extends KindBuilder { 1127 @Override getTagName()1128 public String getTagName() { 1129 return "postal"; 1130 } 1131 1132 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1133 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1134 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1135 IOException { 1136 final DataKind kind = newDataKind(context, parser, attrs, false, 1137 StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, 1138 R.string.postalLabelsGroup, Weight.STRUCTURED_POSTAL, 1139 new PostalActionInflater(), 1140 new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS)); 1141 1142 if (getAttr(attrs, "needsStructured", false)) { 1143 if (Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage())) { 1144 // Japanese order 1145 kind.fieldList.add(new EditField(StructuredPostal.COUNTRY, 1146 R.string.postal_country, FLAGS_POSTAL).setOptional(true)); 1147 kind.fieldList.add(new EditField(StructuredPostal.POSTCODE, 1148 R.string.postal_postcode, FLAGS_POSTAL)); 1149 kind.fieldList.add(new EditField(StructuredPostal.REGION, 1150 R.string.postal_region, FLAGS_POSTAL)); 1151 kind.fieldList.add(new EditField(StructuredPostal.CITY, 1152 R.string.postal_city,FLAGS_POSTAL)); 1153 kind.fieldList.add(new EditField(StructuredPostal.STREET, 1154 R.string.postal_street, FLAGS_POSTAL)); 1155 } else { 1156 // Generic order 1157 kind.fieldList.add(new EditField(StructuredPostal.STREET, 1158 R.string.postal_street, FLAGS_POSTAL)); 1159 kind.fieldList.add(new EditField(StructuredPostal.CITY, 1160 R.string.postal_city,FLAGS_POSTAL)); 1161 kind.fieldList.add(new EditField(StructuredPostal.REGION, 1162 R.string.postal_region, FLAGS_POSTAL)); 1163 kind.fieldList.add(new EditField(StructuredPostal.POSTCODE, 1164 R.string.postal_postcode, FLAGS_POSTAL)); 1165 kind.fieldList.add(new EditField(StructuredPostal.COUNTRY, 1166 R.string.postal_country, FLAGS_POSTAL).setOptional(true)); 1167 } 1168 } else { 1169 kind.maxLinesForDisplay= MAX_LINES_FOR_POSTAL_ADDRESS; 1170 kind.fieldList.add( 1171 new EditField(StructuredPostal.FORMATTED_ADDRESS, R.string.postal_address, 1172 FLAGS_POSTAL)); 1173 } 1174 1175 return Lists.newArrayList(kind); 1176 } 1177 1178 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1179 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1180 // EditType is mutable, so we need to create a new instance every time. 1181 if ("home".equals(type)) return buildPostalType(StructuredPostal.TYPE_HOME); 1182 if ("work".equals(type)) return buildPostalType(StructuredPostal.TYPE_WORK); 1183 if ("other".equals(type)) return buildPostalType(StructuredPostal.TYPE_OTHER); 1184 if ("custom".equals(type)) { 1185 return buildPostalType(StructuredPostal.TYPE_CUSTOM) 1186 .setSecondary(true).setCustomColumn(Email.LABEL); 1187 } 1188 return null; 1189 } 1190 } 1191 1192 private static class ImKindBuilder extends KindBuilder { 1193 @Override getTagName()1194 public String getTagName() { 1195 return "im"; 1196 } 1197 1198 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1199 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1200 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1201 IOException { 1202 1203 // IM is special: 1204 // - It uses "protocol" as the custom label field 1205 // - Its TYPE is fixed to TYPE_OTHER 1206 1207 final DataKind kind = newDataKind(context, parser, attrs, false, 1208 Im.CONTENT_ITEM_TYPE, Im.PROTOCOL, R.string.imLabelsGroup, Weight.IM, 1209 new ImActionInflater(), new SimpleInflater(Im.DATA) // header / action 1210 ); 1211 kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL)); 1212 1213 kind.defaultValues = new ContentValues(); 1214 kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER); 1215 1216 return Lists.newArrayList(kind); 1217 } 1218 1219 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1220 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1221 if ("aim".equals(type)) return buildImType(Im.PROTOCOL_AIM); 1222 if ("msn".equals(type)) return buildImType(Im.PROTOCOL_MSN); 1223 if ("yahoo".equals(type)) return buildImType(Im.PROTOCOL_YAHOO); 1224 if ("skype".equals(type)) return buildImType(Im.PROTOCOL_SKYPE); 1225 if ("qq".equals(type)) return buildImType(Im.PROTOCOL_QQ); 1226 if ("google_talk".equals(type)) return buildImType(Im.PROTOCOL_GOOGLE_TALK); 1227 if ("icq".equals(type)) return buildImType(Im.PROTOCOL_ICQ); 1228 if ("jabber".equals(type)) return buildImType(Im.PROTOCOL_JABBER); 1229 if ("custom".equals(type)) { 1230 return buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true) 1231 .setCustomColumn(Im.CUSTOM_PROTOCOL); 1232 } 1233 return null; 1234 } 1235 } 1236 1237 private static class OrganizationKindBuilder extends KindBuilder { 1238 @Override getTagName()1239 public String getTagName() { 1240 return "organization"; 1241 } 1242 1243 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1244 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1245 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1246 IOException { 1247 final DataKind kind = newDataKind(context, parser, attrs, false, 1248 Organization.CONTENT_ITEM_TYPE, null, R.string.organizationLabelsGroup, 1249 Weight.ORGANIZATION, 1250 new SimpleInflater(R.string.organizationLabelsGroup), 1251 ORGANIZATION_BODY_INFLATER); 1252 1253 kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company, 1254 FLAGS_GENERIC_NAME)); 1255 kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title, 1256 FLAGS_GENERIC_NAME)); 1257 1258 throwIfList(kind); 1259 1260 return Lists.newArrayList(kind); 1261 } 1262 } 1263 1264 private static class PhotoKindBuilder extends KindBuilder { 1265 @Override getTagName()1266 public String getTagName() { 1267 return "photo"; 1268 } 1269 1270 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1271 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1272 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1273 IOException { 1274 final DataKind kind = newDataKind(context, parser, attrs, false, 1275 Photo.CONTENT_ITEM_TYPE, null /* no type */, Weight.NONE, -1, 1276 null, null // no header, no body 1277 ); 1278 1279 kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1)); 1280 1281 throwIfList(kind); 1282 1283 return Lists.newArrayList(kind); 1284 } 1285 } 1286 1287 private static class NoteKindBuilder extends KindBuilder { 1288 @Override getTagName()1289 public String getTagName() { 1290 return "note"; 1291 } 1292 1293 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1294 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1295 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1296 IOException { 1297 final DataKind kind = newDataKind(context, parser, attrs, false, 1298 Note.CONTENT_ITEM_TYPE, null, R.string.label_notes, Weight.NOTE, 1299 new SimpleInflater(R.string.label_notes), new SimpleInflater(Note.NOTE)); 1300 1301 kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE)); 1302 kind.maxLinesForDisplay = MAX_LINES_FOR_NOTE; 1303 1304 throwIfList(kind); 1305 1306 return Lists.newArrayList(kind); 1307 } 1308 } 1309 1310 private static class WebsiteKindBuilder extends KindBuilder { 1311 @Override getTagName()1312 public String getTagName() { 1313 return "website"; 1314 } 1315 1316 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1317 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1318 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1319 IOException { 1320 final DataKind kind = newDataKind(context, parser, attrs, false, 1321 Website.CONTENT_ITEM_TYPE, null, R.string.websiteLabelsGroup, Weight.WEBSITE, 1322 new SimpleInflater(R.string.websiteLabelsGroup), 1323 new SimpleInflater(Website.URL)); 1324 1325 kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, 1326 FLAGS_WEBSITE)); 1327 1328 kind.defaultValues = new ContentValues(); 1329 kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER); 1330 1331 return Lists.newArrayList(kind); 1332 } 1333 } 1334 1335 private static class SipAddressKindBuilder extends KindBuilder { 1336 @Override getTagName()1337 public String getTagName() { 1338 return "sip_address"; 1339 } 1340 1341 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1342 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1343 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1344 IOException { 1345 final DataKind kind = newDataKind(context, parser, attrs, false, 1346 SipAddress.CONTENT_ITEM_TYPE, null, R.string.label_sip_address, 1347 Weight.SIP_ADDRESS, 1348 new SimpleInflater(R.string.label_sip_address), 1349 new SimpleInflater(SipAddress.SIP_ADDRESS)); 1350 1351 kind.fieldList.add(new EditField(SipAddress.SIP_ADDRESS, 1352 R.string.label_sip_address, FLAGS_SIP_ADDRESS)); 1353 1354 throwIfList(kind); 1355 1356 return Lists.newArrayList(kind); 1357 } 1358 } 1359 1360 private static class GroupMembershipKindBuilder extends KindBuilder { 1361 @Override getTagName()1362 public String getTagName() { 1363 return "group_membership"; 1364 } 1365 1366 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1367 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1368 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1369 IOException { 1370 final DataKind kind = newDataKind(context, parser, attrs, false, 1371 GroupMembership.CONTENT_ITEM_TYPE, null, 1372 R.string.groupsLabel, Weight.GROUP_MEMBERSHIP, null, null); 1373 1374 kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1)); 1375 kind.maxLinesForDisplay = MAX_LINES_FOR_GROUP; 1376 1377 throwIfList(kind); 1378 1379 return Lists.newArrayList(kind); 1380 } 1381 } 1382 1383 /** 1384 * Event DataKind parser. 1385 * 1386 * Event DataKind is used only for Google/Exchange types, so this parser is not used for now. 1387 */ 1388 private static class EventKindBuilder extends KindBuilder { 1389 @Override getTagName()1390 public String getTagName() { 1391 return "event"; 1392 } 1393 1394 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1395 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1396 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1397 IOException { 1398 final DataKind kind = newDataKind(context, parser, attrs, false, 1399 Event.CONTENT_ITEM_TYPE, Event.TYPE, R.string.eventLabelsGroup, Weight.EVENT, 1400 new EventActionInflater(), new SimpleInflater(Event.START_DATE)); 1401 1402 kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT)); 1403 1404 if (getAttr(attrs, Attr.DATE_WITH_TIME, false)) { 1405 kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_AND_TIME_FORMAT; 1406 kind.dateFormatWithYear = CommonDateUtils.DATE_AND_TIME_FORMAT; 1407 } else { 1408 kind.dateFormatWithoutYear = CommonDateUtils.NO_YEAR_DATE_FORMAT; 1409 kind.dateFormatWithYear = CommonDateUtils.FULL_DATE_FORMAT; 1410 } 1411 1412 return Lists.newArrayList(kind); 1413 } 1414 1415 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1416 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1417 final boolean yo = getAttr(attrs, Attr.YEAR_OPTIONAL, false); 1418 1419 if ("birthday".equals(type)) { 1420 return buildEventType(Event.TYPE_BIRTHDAY, yo).setSpecificMax(1); 1421 } 1422 if ("anniversary".equals(type)) return buildEventType(Event.TYPE_ANNIVERSARY, yo); 1423 if ("other".equals(type)) return buildEventType(Event.TYPE_OTHER, yo); 1424 if ("custom".equals(type)) { 1425 return buildEventType(Event.TYPE_CUSTOM, yo) 1426 .setSecondary(true).setCustomColumn(Event.LABEL); 1427 } 1428 return null; 1429 } 1430 } 1431 1432 /** 1433 * Relationship DataKind parser. 1434 * 1435 * Relationship DataKind is used only for Google/Exchange types, so this parser is not used for 1436 * now. 1437 */ 1438 private static class RelationshipKindBuilder extends KindBuilder { 1439 @Override getTagName()1440 public String getTagName() { 1441 return "relationship"; 1442 } 1443 1444 @Override parseDataKind(Context context, XmlPullParser parser, AttributeSet attrs)1445 public List<DataKind> parseDataKind(Context context, XmlPullParser parser, 1446 AttributeSet attrs) throws DefinitionException, XmlPullParserException, 1447 IOException { 1448 final DataKind kind = newDataKind(context, parser, attrs, false, 1449 Relation.CONTENT_ITEM_TYPE, Relation.TYPE, 1450 R.string.relationLabelsGroup, Weight.RELATIONSHIP, 1451 new RelationActionInflater(), new SimpleInflater(Relation.NAME)); 1452 1453 kind.fieldList.add(new EditField(Relation.DATA, R.string.relationLabelsGroup, 1454 FLAGS_RELATION)); 1455 1456 kind.defaultValues = new ContentValues(); 1457 kind.defaultValues.put(Relation.TYPE, Relation.TYPE_SPOUSE); 1458 1459 return Lists.newArrayList(kind); 1460 } 1461 1462 @Override buildEditTypeForTypeTag(AttributeSet attrs, String type)1463 protected EditType buildEditTypeForTypeTag(AttributeSet attrs, String type) { 1464 // EditType is mutable, so we need to create a new instance every time. 1465 if ("assistant".equals(type)) return buildRelationType(Relation.TYPE_ASSISTANT); 1466 if ("brother".equals(type)) return buildRelationType(Relation.TYPE_BROTHER); 1467 if ("child".equals(type)) return buildRelationType(Relation.TYPE_CHILD); 1468 if ("domestic_partner".equals(type)) { 1469 return buildRelationType(Relation.TYPE_DOMESTIC_PARTNER); 1470 } 1471 if ("father".equals(type)) return buildRelationType(Relation.TYPE_FATHER); 1472 if ("friend".equals(type)) return buildRelationType(Relation.TYPE_FRIEND); 1473 if ("manager".equals(type)) return buildRelationType(Relation.TYPE_MANAGER); 1474 if ("mother".equals(type)) return buildRelationType(Relation.TYPE_MOTHER); 1475 if ("parent".equals(type)) return buildRelationType(Relation.TYPE_PARENT); 1476 if ("partner".equals(type)) return buildRelationType(Relation.TYPE_PARTNER); 1477 if ("referred_by".equals(type)) return buildRelationType(Relation.TYPE_REFERRED_BY); 1478 if ("relative".equals(type)) return buildRelationType(Relation.TYPE_RELATIVE); 1479 if ("sister".equals(type)) return buildRelationType(Relation.TYPE_SISTER); 1480 if ("spouse".equals(type)) return buildRelationType(Relation.TYPE_SPOUSE); 1481 if ("custom".equals(type)) { 1482 return buildRelationType(Relation.TYPE_CUSTOM).setSecondary(true) 1483 .setCustomColumn(Relation.LABEL); 1484 } 1485 return null; 1486 } 1487 } 1488 } 1489