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