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