Merge "Add api to disable BackgroundImageView animation." into pi-car-dev
diff --git a/car-telephony-common/src/com/android/car/telephony/common/Contact.java b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
index 69904b4..2f6212d 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/Contact.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
@@ -37,128 +37,80 @@
*/
public class Contact implements Parcelable, Comparable<Contact> {
private static final String TAG = "CD.Contact";
+
+ /**
+ * Column name for phonebook label column.
+ */
private static final String PHONEBOOK_LABEL = "phonebook_label";
+ /**
+ * Column name for alternative phonebook label column.
+ */
private static final String PHONEBOOK_LABEL_ALT = "phonebook_label_alt";
/**
* Contact belongs to TYPE_LETTER if its display name starts with a letter
*/
private static final int TYPE_LETTER = 1;
-
/**
* Contact belongs to TYPE_DIGIT if its display name starts with a digit
*/
private static final int TYPE_DIGIT = 2;
-
/**
- * Contact belongs to TYPE_OTHER if it does not belong to TYPE_LETTER or TYPE_DIGIT
- * Such as empty display name or the display name starts with "_"
+ * Contact belongs to TYPE_OTHER if it does not belong to TYPE_LETTER or TYPE_DIGIT Such as
+ * empty display name or the display name starts with "_"
*/
private static final int TYPE_OTHER = 3;
+ /**
+ * A reference to the {@link ContactsContract.RawContacts#CONTACT_ID}.
+ */
+ private long mContactId;
/**
- * A reference to the {@link ContactsContract.Contacts#_ID} that this data belongs to. See
- * {@link ContactsContract.Contacts.Entity#CONTACT_ID}
+ * A reference to the {@link ContactsContract.Data#RAW_CONTACT_ID}.
*/
- private long mId;
+ private long mRawContactId;
/**
- * Whether this contact entry is starred by user.
+ * The name of the account instance to which this row belongs, which identifies a specific
+ * account. See {@link ContactsContract.RawContacts#ACCOUNT_NAME}.
*/
- private boolean mIsStarred;
-
- /**
- * Contact-specific information about whether or not a contact has been pinned by the user at
- * a particular position within the system contact application's user interface.
- */
- private int mPinnedPosition;
-
- /**
- * All phone numbers of this contact mapping to the unique primary key for the raw data entry.
- */
- private List<PhoneNumber> mPhoneNumbers = new ArrayList<>();
+ private String mAccountName;
/**
* The display name.
* <p>
- * The standard text shown as the contact's display name, based on the best
- * available information for the contact.
+ * The standard text shown as the contact's display name, based on the best available
+ * information for the contact.
* </p>
- *
- * @see ContactsContract.CommonDataKinds.Phone#DISPLAY_NAME
+ * <p>
+ * See {@link ContactsContract.CommonDataKinds.Phone#DISPLAY_NAME}.
*/
private String mDisplayName;
/**
- * Sort key that takes into account locale-based traditions for sorting names in address books.
- *
- * @see ContactsContract.CommonDataKinds.Phone#SORT_KEY_PRIMARY
- */
- private String mSortKeyPrimary;
-
- /**
* The alternative display name.
* <p>
- * An alternative representation of the display name, such as "family name first"
- * instead of "given name first" for Western names. If an alternative is not
- * available, the values should be the same as {@link #mDisplayName}.
+ * An alternative representation of the display name, such as "family name first" instead of
+ * "given name first" for Western names. If an alternative is not available, the values should
+ * be the same as {@link #mDisplayName}.
* </p>
- *
- * @see ContactsContract.CommonDataKinds.Phone#DISPLAY_NAME_ALTERNATIVE
- */
- private String mAltDisplayName;
-
- /**
- * Sort key based on the alternative representation of the full name.
- *
- * @see ContactsContract.CommonDataKinds.Phone#SORT_KEY_ALTERNATIVE
- */
- private String mAltSortKey;
-
- /**
- * The phonebook label.
* <p>
- * For {@link #mDisplayName}s starting with letters, label will be the first character of
- * {@link #mDisplayName}. For {@link #mDisplayName}s starting with numbers, the label will
- * be "#". For {@link #mDisplayName}s starting with other characters, the label will be "...".
- * </p>
+ * See {@link ContactsContract.CommonDataKinds.Phone#DISPLAY_NAME_ALTERNATIVE}.
*/
- private String mPhoneBookLabel;
+ private String mDisplayNameAlt;
/**
- * The alternative phonebook label.
- * <p>
- * It is similar with {@link #mPhoneBookLabel}. But instead of generating from
- * {@link #mDisplayName}, it will use {@link #mAltDisplayName}.
- * </p>
+ * The given name for the contact. See
+ * {@link ContactsContract.CommonDataKinds.StructuredName#GIVEN_NAME}.
*/
- private String mPhoneBookLabelAlt;
+ private String mGivenName;
/**
- * A URI that can be used to retrieve a thumbnail of the contact's photo.
+ * The family name for the contact. See
+ * {@link ContactsContract.CommonDataKinds.StructuredName#FAMILY_NAME}.
*/
- private Uri mAvatarThumbnailUri;
-
- /**
- * A URI that can be used to retrieve the contact's full-size photo.
- */
- private Uri mAvatarUri;
-
- /**
- * An opaque value that contains hints on how to find the contact if its row id changed
- * as a result of a sync or aggregation. If a contact has multiple phone numbers, all phone
- * numbers are recorded in a single entry and they all have the same look up key in a single
- * load.
- */
- private String mLookupKey;
-
- /**
- * Whether this contact represents a voice mail.
- */
- private boolean mIsVoiceMail;
-
- private PhoneNumber mPrimaryPhoneNumber;
+ private String mFamilyName;
/**
* The initials of the contact's name.
@@ -166,69 +118,221 @@
private String mInitials;
/**
- * Parses a Contact entry for a Cursor loaded from the Contact Database.
+ * The phonebook label.
+ * <p>
+ * For {@link #mDisplayName}s starting with letters, label will be the first character of {@link
+ * #mDisplayName}. For {@link #mDisplayName}s starting with numbers, the label will be "#". For
+ * {@link #mDisplayName}s starting with other characters, the label will be "...".
+ * </p>
+ */
+ private String mPhoneBookLabel;
+
+ /**
+ * The alternative phonebook label.
+ * <p>
+ * It is similar with {@link #mPhoneBookLabel}. But instead of generating from {@link
+ * #mDisplayName}, it will use {@link #mDisplayNameAlt}.
+ * </p>
+ */
+ private String mPhoneBookLabelAlt;
+
+ /**
+ * Sort key that takes into account locale-based traditions for sorting names in address books.
+ * <p>
+ * See {@link ContactsContract.CommonDataKinds.Phone#SORT_KEY_PRIMARY}.
+ */
+ private String mSortKeyPrimary;
+
+ /**
+ * Sort key based on the alternative representation of the full name.
+ * <p>
+ * See {@link ContactsContract.CommonDataKinds.Phone#SORT_KEY_ALTERNATIVE}.
+ */
+ private String mSortKeyAlt;
+
+ /**
+ * An opaque value that contains hints on how to find the contact if its row id changed as a
+ * result of a sync or aggregation. If a contact has multiple phone numbers, all phone numbers
+ * are recorded in a single entry and they all have the same look up key in a single load. See
+ * {@link ContactsContract.Data#LOOKUP_KEY}.
+ */
+ private String mLookupKey;
+
+ /**
+ * All phone numbers of this contact mapping to the unique primary key for the raw data entry.
+ */
+ private List<PhoneNumber> mPhoneNumbers = new ArrayList<>();
+
+ /**
+ * A URI that can be used to retrieve a thumbnail of the contact's photo.
+ */
+ @Nullable
+ private Uri mAvatarThumbnailUri;
+
+ /**
+ * A URI that can be used to retrieve the contact's full-size photo.
+ */
+ @Nullable
+ private Uri mAvatarUri;
+
+ /**
+ * Whether this contact entry is starred by user.
+ */
+ private boolean mIsStarred;
+
+ /**
+ * Contact-specific information about whether or not a contact has been pinned by the user at a
+ * particular position within the system contact application's user interface.
+ */
+ private int mPinnedPosition;
+
+ /**
+ * This contact's primary phone number. Its value is null if a primary phone number is not set.
+ */
+ @Nullable
+ private PhoneNumber mPrimaryPhoneNumber;
+
+ /**
+ * Whether this contact represents a voice mail.
+ */
+ private boolean mIsVoiceMail;
+
+ /**
+ * Parses a contact entry for a Cursor loaded from the Contact Database. A new contact will be
+ * created and returned.
*/
public static Contact fromCursor(Context context, Cursor cursor) {
- int contactIdColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
- int starredColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED);
- int pinnedColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PINNED);
- int displayNameColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
- int altDisplayNameColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_ALTERNATIVE);
+ return fromCursor(context, cursor, null);
+ }
+
+ /**
+ * Parses a contact entry for a Cursor loaded from the Contact Database.
+ *
+ * @param contact should have the same {@link #mLookupKey} and {@link #mAccountName} with the
+ * data read from the cursor, so all the data from the cursor can be loaded into
+ * this contact. If either of their {@link #mLookupKey} and {@link #mAccountName}
+ * is not the same or this contact is null, a new contact will be created and
+ * returned.
+ */
+ public static Contact fromCursor(Context context, Cursor cursor, @Nullable Contact contact) {
+ int accountNameColumn = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME);
+ int lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ String accountName = cursor.getString(accountNameColumn);
+ String lookupKey = cursor.getString(lookupKeyColumn);
+
+ if (contact == null) {
+ Log.d(TAG, "A new contact will be created.");
+ contact = new Contact();
+ contact.loadBasicInfo(cursor);
+ }
+
+ if (!accountName.equals(contact.mAccountName) || !lookupKey.equals(contact.mLookupKey)) {
+ Log.w(TAG, "A wrong contact is passed in. A new contact will be created.");
+ contact = new Contact();
+ contact.loadBasicInfo(cursor);
+ }
+
+ int mimetypeColumn = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
+ String mimeType = cursor.getString(mimetypeColumn);
+
+ // More mimeType can be added here if more types of data needs to be loaded.
+ switch (mimeType) {
+ case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:
+ contact.loadNameDetails(cursor);
+ break;
+ case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
+ contact.addPhoneNumber(context, cursor);
+ break;
+ default:
+ Log.d(TAG,
+ String.format("This mimetype %s will not be loaded right now.", mimeType));
+ }
+
+ return contact;
+ }
+
+ /**
+ * The data columns that are the same in every cursor no matter what the mimetype is will be
+ * loaded here.
+ */
+ private void loadBasicInfo(Cursor cursor) {
+ int contactIdColumn = cursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
+ int rawContactIdColumn = cursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
+ int accountNameColumn = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME);
+ int displayNameColumn = cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
+ int displayNameAltColumn = cursor.getColumnIndex(
+ ContactsContract.RawContacts.DISPLAY_NAME_ALTERNATIVE);
int phoneBookLabelColumn = cursor.getColumnIndex(PHONEBOOK_LABEL);
int phoneBookLabelAltColumn = cursor.getColumnIndex(PHONEBOOK_LABEL_ALT);
- int avatarUriColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.PHOTO_URI);
- int avatarThumbnailColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.PHOTO_THUMBNAIL_URI);
- int lookupKeyColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY);
int sortKeyPrimaryColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.SORT_KEY_PRIMARY);
+ ContactsContract.RawContacts.SORT_KEY_PRIMARY);
int sortKeyAltColumn = cursor.getColumnIndex(
- ContactsContract.CommonDataKinds.Phone.SORT_KEY_ALTERNATIVE);
+ ContactsContract.RawContacts.SORT_KEY_ALTERNATIVE);
+ int lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
- Contact contact = new Contact();
- contact.mId = cursor.getLong(contactIdColumn);
- contact.mDisplayName = cursor.getString(displayNameColumn);
- contact.mSortKeyPrimary = cursor.getString(sortKeyPrimaryColumn);
- contact.mAltDisplayName = cursor.getString(altDisplayNameColumn);
- contact.mAltSortKey = cursor.getString(sortKeyAltColumn);
- contact.mPhoneBookLabel = cursor.getString(phoneBookLabelColumn);
- contact.mPhoneBookLabelAlt = cursor.getString(phoneBookLabelAltColumn);
+ int avatarUriColumn = cursor.getColumnIndex(ContactsContract.Data.PHOTO_URI);
+ int avatarThumbnailColumn = cursor.getColumnIndex(
+ ContactsContract.Data.PHOTO_THUMBNAIL_URI);
+ int starredColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED);
+ int pinnedColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PINNED);
- PhoneNumber number = PhoneNumber.fromCursor(context, cursor);
- contact.mPhoneNumbers.add(number);
- if (number.isPrimary()) {
- contact.mPrimaryPhoneNumber = number;
- }
-
- contact.mIsStarred = cursor.getInt(starredColumn) > 0;
- contact.mPinnedPosition = cursor.getInt(pinnedColumn);
- contact.mIsVoiceMail = TelecomUtils.isVoicemailNumber(context, number.getNumber());
+ mContactId = cursor.getLong(contactIdColumn);
+ mRawContactId = cursor.getLong(rawContactIdColumn);
+ mAccountName = cursor.getString(accountNameColumn);
+ mDisplayName = cursor.getString(displayNameColumn);
+ mDisplayNameAlt = cursor.getString(displayNameAltColumn);
+ mSortKeyPrimary = cursor.getString(sortKeyPrimaryColumn);
+ mSortKeyAlt = cursor.getString(sortKeyAltColumn);
+ mPhoneBookLabel = cursor.getString(phoneBookLabelColumn);
+ mPhoneBookLabelAlt = cursor.getString(phoneBookLabelAltColumn);
+ mLookupKey = cursor.getString(lookupKeyColumn);
String avatarUriStr = cursor.getString(avatarUriColumn);
- contact.mAvatarUri = avatarUriStr == null ? null : Uri.parse(avatarUriStr);
-
+ mAvatarUri = avatarUriStr == null ? null : Uri.parse(avatarUriStr);
String avatarThumbnailStringUri = cursor.getString(avatarThumbnailColumn);
- contact.mAvatarThumbnailUri = avatarThumbnailStringUri == null ? null : Uri.parse(
+ mAvatarThumbnailUri = avatarThumbnailStringUri == null ? null : Uri.parse(
avatarThumbnailStringUri);
- String lookUpKey = cursor.getString(lookupKeyColumn);
- if (lookUpKey != null) {
- contact.mLookupKey = lookUpKey;
- } else {
- Log.w(TAG, "Look up key is null. Fallback to use display name");
- contact.mLookupKey = contact.mDisplayName;
+ mIsStarred = cursor.getInt(starredColumn) > 0;
+ mPinnedPosition = cursor.getInt(pinnedColumn);
+ }
+
+ /**
+ * Loads the data whose mimetype is
+ * {@link ContactsContract.CommonDataKinds.StructuredName#CONTENT_ITEM_TYPE}.
+ */
+ private void loadNameDetails(Cursor cursor) {
+ int firstNameColumn = cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
+ int lastNameColumn = cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
+
+ mGivenName = cursor.getString(firstNameColumn);
+ mFamilyName = cursor.getString(lastNameColumn);
+ }
+
+ /**
+ * Loads the data whose mimetype is
+ * {@link ContactsContract.CommonDataKinds.Phone#CONTENT_ITEM_TYPE}.
+ */
+ private void addPhoneNumber(Context context, Cursor cursor) {
+ PhoneNumber number = PhoneNumber.fromCursor(context, cursor);
+ mPhoneNumbers.add(number);
+
+ if (number.isPrimary()) {
+ mPrimaryPhoneNumber = number;
}
- return contact;
+
+ // TODO: update voice mail number part when start to support voice mail.
+ if (TelecomUtils.isVoicemailNumber(context, number.getNumber())) {
+ mIsVoiceMail = true;
+ }
}
@Override
public boolean equals(Object obj) {
- return obj instanceof Contact && mLookupKey.equals(((Contact) obj).mLookupKey);
+ return obj instanceof Contact && mLookupKey.equals(((Contact) obj).mLookupKey)
+ && mAccountName.equals(((Contact) obj).mAccountName);
}
@Override
@@ -241,15 +345,76 @@
return mDisplayName + mPhoneNumbers;
}
+ /**
+ * Returns the aggregated contact id.
+ */
+ public long getId() {
+ return mContactId;
+ }
+
+ /**
+ * Returns the raw contact id.
+ */
+ public long getRawContactId() {
+ return mRawContactId;
+ }
+
+ /**
+ * Returns a lookup uri using {@link #mContactId} and {@link #mLookupKey}. Returns null if
+ * unable to get a valid lookup URI from the provided parameters. See {@link
+ * ContactsContract.Contacts#getLookupUri(long, String)}.
+ */
+ @Nullable
+ public Uri getLookupUri() {
+ return ContactsContract.Contacts.getLookupUri(mContactId, mLookupKey);
+ }
+
+ /**
+ * Returns {@link #mAccountName}.
+ */
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ /**
+ * Returns {@link #mDisplayName}.
+ */
public String getDisplayName() {
return mDisplayName;
}
/**
- * Returns alternative display name.
+ * Returns {@link #mDisplayNameAlt}.
*/
- public String getAltDisplayName() {
- return mAltDisplayName;
+ public String getDisplayNameAlt() {
+ return mDisplayNameAlt;
+ }
+
+ /**
+ * Returns {@link #mGivenName}.
+ */
+ public String getGivenName() {
+ return mGivenName;
+ }
+
+ /**
+ * Returns {@link #mFamilyName}.
+ */
+ public String getFamilyName() {
+ return mFamilyName;
+ }
+
+ /**
+ * Returns the initials of the contact's name.
+ */
+ //TODO: update how to get initials after refactoring. Could use last name and first name to
+ // get initials after refactoring to avoid error for those names with prefix.
+ public String getInitials() {
+ if (mInitials == null) {
+ mInitials = TelecomUtils.getInitials(mDisplayName, mDisplayNameAlt);
+ }
+
+ return mInitials;
}
/**
@@ -266,80 +431,67 @@
return mPhoneBookLabelAlt;
}
- public boolean isVoicemail() {
- return mIsVoiceMail;
- }
-
- @Nullable
- public Uri getAvatarUri() {
- return mAvatarThumbnailUri != null ? mAvatarThumbnailUri : mAvatarUri;
- }
-
+ /**
+ * Returns {@link #mLookupKey}.
+ */
public String getLookupKey() {
return mLookupKey;
}
- public Uri getLookupUri() {
- return ContactsContract.Contacts.getLookupUri(mId, mLookupKey);
+ /**
+ * Returns the Uri for avatar.
+ */
+ @Nullable
+ public Uri getAvatarUri() {
+ return mAvatarUri != null ? mAvatarUri : mAvatarThumbnailUri;
}
- /** Return all phone numbers associated with this contact. */
+ /**
+ * Return all phone numbers associated with this contact.
+ */
public List<PhoneNumber> getNumbers() {
return mPhoneNumbers;
}
- /** Return the aggregated contact id. */
- public long getId() {
- return mId;
+ /**
+ * Returns if this Contact represents a voice mail number.
+ */
+ public boolean isVoicemail() {
+ return mIsVoiceMail;
}
+ /**
+ * Returns if this contact has a primary phone number.
+ */
+ public boolean hasPrimaryPhoneNumber() {
+ return mPrimaryPhoneNumber != null;
+ }
+
+ /**
+ * Returns the primary phone number for this Contact. Returns null if there is not one.
+ */
+ @Nullable
+ public PhoneNumber getPrimaryPhoneNumber() {
+ return mPrimaryPhoneNumber;
+ }
+
+ /**
+ * Returns if this Contact is starred.
+ */
public boolean isStarred() {
return mIsStarred;
}
+ /**
+ * Returns {@link #mPinnedPosition}.
+ */
public int getPinnedPosition() {
return mPinnedPosition;
}
/**
- * Returns the initials of the contact's name.
- */
- //TODO: update how to get initials after refactoring. Could use last name and first name to
- // get initials after refactoring to avoid error for those names with prefix.
- public String getInitials() {
- if (mInitials == null) {
- mInitials = TelecomUtils.getInitials(mDisplayName, mAltDisplayName);
- }
-
- return mInitials;
- }
-
- /**
- * Merges a Contact entry with another if they represent different numbers of the same contact.
- *
- * @return A merged contact.
- */
- public Contact merge(Contact contact) {
- if (equals(contact)) {
- for (PhoneNumber phoneNumber : contact.mPhoneNumbers) {
- int indexOfPhoneNumber = mPhoneNumbers.indexOf(phoneNumber);
- if (indexOfPhoneNumber < 0) {
- mPhoneNumbers.add(phoneNumber);
- } else {
- PhoneNumber existingPhoneNumber = mPhoneNumbers.get(indexOfPhoneNumber);
- existingPhoneNumber.merge(phoneNumber);
- }
- }
- if (contact.mPrimaryPhoneNumber != null) {
- mPrimaryPhoneNumber = contact.mPrimaryPhoneNumber.merge(mPrimaryPhoneNumber);
- }
- }
- return this;
- }
-
- /**
- * Looks up a {@link PhoneNumber} of this contact for the given phone number. Returns {@code
- * null} if this contact doesn't contain the given phone number.
+ * Looks up a {@link PhoneNumber} of this contact for the given phone number string. Returns
+ * {@code null} if this contact doesn't contain the given phone number.
*/
@Nullable
public PhoneNumber getPhoneNumber(Context context, String number) {
@@ -353,15 +505,6 @@
return null;
}
- public PhoneNumber getPrimaryPhoneNumber() {
- return mPrimaryPhoneNumber;
- }
-
- /** Return if this contact has a primary phone number. */
- public boolean hasPrimaryPhoneNumber() {
- return mPrimaryPhoneNumber != null;
- }
-
@Override
public int describeContents() {
return 0;
@@ -369,23 +512,27 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mId);
- dest.writeBoolean(mIsStarred);
- dest.writeInt(mPinnedPosition);
- dest.writeInt(mPhoneNumbers.size());
- for (PhoneNumber phoneNumber : mPhoneNumbers) {
- dest.writeParcelable(phoneNumber, flags);
- }
+ dest.writeLong(mContactId);
+ dest.writeLong(mRawContactId);
+ dest.writeString(mLookupKey);
+ dest.writeString(mAccountName);
dest.writeString(mDisplayName);
+ dest.writeString(mDisplayNameAlt);
dest.writeString(mSortKeyPrimary);
- dest.writeString(mAltDisplayName);
- dest.writeString(mAltSortKey);
+ dest.writeString(mSortKeyAlt);
dest.writeString(mPhoneBookLabel);
dest.writeString(mPhoneBookLabelAlt);
dest.writeParcelable(mAvatarThumbnailUri, 0);
dest.writeParcelable(mAvatarUri, 0);
- dest.writeString(mLookupKey);
+ dest.writeBoolean(mIsStarred);
+ dest.writeInt(mPinnedPosition);
+
dest.writeBoolean(mIsVoiceMail);
+ dest.writeParcelable(mPrimaryPhoneNumber, flags);
+ dest.writeInt(mPhoneNumbers.size());
+ for (PhoneNumber phoneNumber : mPhoneNumbers) {
+ dest.writeParcelable(phoneNumber, flags);
+ }
}
public static final Creator<Contact> CREATOR = new Creator<Contact>() {
@@ -400,12 +547,28 @@
}
};
- /** Create {@link Contact} object from saved parcelable. */
+ /**
+ * Create {@link Contact} object from saved parcelable.
+ */
private static Contact fromParcel(Parcel source) {
Contact contact = new Contact();
- contact.mId = source.readLong();
+ contact.mContactId = source.readLong();
+ contact.mRawContactId = source.readLong();
+ contact.mLookupKey = source.readString();
+ contact.mAccountName = source.readString();
+ contact.mDisplayName = source.readString();
+ contact.mDisplayNameAlt = source.readString();
+ contact.mSortKeyPrimary = source.readString();
+ contact.mSortKeyAlt = source.readString();
+ contact.mPhoneBookLabel = source.readString();
+ contact.mPhoneBookLabelAlt = source.readString();
+ contact.mAvatarThumbnailUri = source.readParcelable(Uri.class.getClassLoader());
+ contact.mAvatarUri = source.readParcelable(Uri.class.getClassLoader());
contact.mIsStarred = source.readBoolean();
contact.mPinnedPosition = source.readInt();
+
+ contact.mIsVoiceMail = source.readBoolean();
+ contact.mPrimaryPhoneNumber = source.readParcelable(PhoneNumber.class.getClassLoader());
int phoneNumberListLength = source.readInt();
contact.mPhoneNumbers = new ArrayList<>();
for (int i = 0; i < phoneNumberListLength; i++) {
@@ -415,16 +578,7 @@
contact.mPrimaryPhoneNumber = phoneNumber;
}
}
- contact.mDisplayName = source.readString();
- contact.mSortKeyPrimary = source.readString();
- contact.mAltDisplayName = source.readString();
- contact.mAltSortKey = source.readString();
- contact.mPhoneBookLabel = source.readString();
- contact.mPhoneBookLabelAlt = source.readString();
- contact.mAvatarThumbnailUri = source.readParcelable(Uri.class.getClassLoader());
- contact.mAvatarUri = source.readParcelable(Uri.class.getClassLoader());
- contact.mLookupKey = source.readString();
- contact.mIsVoiceMail = source.readBoolean();
+
return contact;
}
@@ -432,24 +586,24 @@
public int compareTo(Contact otherContact) {
// Use a helper function to classify Contacts
// and by default, it should be compared by first name order.
- return compareByDisplayName(otherContact);
+ return compareBySortKeyPrimary(otherContact);
}
/**
- * Compares contacts by their {@link #mDisplayName} in an order of
- * letters, numbers, then special characters.
+ * Compares contacts by their {@link #mSortKeyPrimary} in an order of letters, numbers, then
+ * special characters.
*/
- public int compareByDisplayName(@NonNull Contact otherContact) {
- return compareNames(mSortKeyPrimary, otherContact.mSortKeyPrimary, mPhoneBookLabel,
- otherContact.getPhonebookLabel());
+ public int compareBySortKeyPrimary(@NonNull Contact otherContact) {
+ return compareNames(mSortKeyPrimary, otherContact.mSortKeyPrimary,
+ mPhoneBookLabel, otherContact.getPhonebookLabel());
}
/**
- * Compares contacts by their {@link #mAltDisplayName} in an order of
- * letters, numbers, then special characters.
+ * Compares contacts by their {@link #mSortKeyAlt} in an order of letters, numbers, then special
+ * characters.
*/
- public int compareByAltDisplayName(@NonNull Contact otherContact) {
- return compareNames(mAltSortKey, otherContact.mAltSortKey,
+ public int compareBySortKeyAlt(@NonNull Contact otherContact) {
+ return compareNames(mSortKeyAlt, otherContact.mSortKeyAlt,
mPhoneBookLabelAlt, otherContact.getPhonebookLabelAlt());
}
@@ -467,8 +621,8 @@
}
/**
- * Returns the type of the name string.
- * Types can be {@link #TYPE_LETTER}, {@link #TYPE_DIGIT} and {@link #TYPE_OTHER}.
+ * Returns the type of the name string. Types can be {@link #TYPE_LETTER}, {@link #TYPE_DIGIT}
+ * and {@link #TYPE_OTHER}.
*/
private static int getNameType(String label) {
// A helper function to classify Contacts
diff --git a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
index 68cefad..db95960 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
@@ -34,25 +34,30 @@
import java.util.concurrent.Executors;
/**
- * A singleton statically accessible helper class which pre-loads contacts list into memory so
- * that they can be accessed more easily and quickly.
+ * A singleton statically accessible helper class which pre-loads contacts list into memory so that
+ * they can be accessed more easily and quickly.
*/
public class InMemoryPhoneBook implements Observer<List<Contact>> {
private static final String TAG = "CD.InMemoryPhoneBook";
+ private static final String KEY_FORMAT = "%s %s";
private static InMemoryPhoneBook sInMemoryPhoneBook;
private final Context mContext;
private final AsyncQueryLiveData<List<Contact>> mContactListAsyncQueryLiveData;
- /** A map to speed up phone number searching. */
+ /**
+ * A map to speed up phone number searching.
+ */
private final Map<I18nPhoneNumberWrapper, Contact> mPhoneNumberContactMap = new HashMap<>();
- /** A map to look up contact by lookup key. */
+ /**
+ * A map to look up contact by lookup key.
+ */
private final Map<String, Contact> mLookupKeyContactMap = new HashMap<>();
private boolean mIsLoaded = false;
/**
- * Initialize the globally accessible {@link InMemoryPhoneBook}.
- * Returns the existing {@link InMemoryPhoneBook} if already initialized.
- * {@link #tearDown()} must be called before init to reinitialize.
+ * Initialize the globally accessible {@link InMemoryPhoneBook}. Returns the existing {@link
+ * InMemoryPhoneBook} if already initialized. {@link #tearDown()} must be called before init to
+ * reinitialize.
*/
public static InMemoryPhoneBook init(Context context) {
if (sInMemoryPhoneBook == null) {
@@ -63,17 +68,18 @@
}
/**
- * Returns if the InMemoryPhoneBook is initialized.
- * get() won't return null or throw if this is true, but it doesn't
- * indicate whether or not contacts are loaded yet.
- *
+ * Returns if the InMemoryPhoneBook is initialized. get() won't return null or throw if this is
+ * true, but it doesn't indicate whether or not contacts are loaded yet.
+ * <p>
* See also: {@link #isLoaded()}
*/
public static boolean isInitialized() {
return sInMemoryPhoneBook != null;
}
- /** Get the global {@link InMemoryPhoneBook} instance. */
+ /**
+ * Get the global {@link InMemoryPhoneBook} instance.
+ */
public static InMemoryPhoneBook get() {
if (sInMemoryPhoneBook != null) {
return sInMemoryPhoneBook;
@@ -82,7 +88,9 @@
}
}
- /** Tears down the globally accessible {@link InMemoryPhoneBook}. */
+ /**
+ * Tears down the globally accessible {@link InMemoryPhoneBook}.
+ */
public static void tearDown() {
sInMemoryPhoneBook.onTearDown();
sInMemoryPhoneBook = null;
@@ -94,9 +102,11 @@
QueryParam contactListQueryParam = new QueryParam(
ContactsContract.Data.CONTENT_URI,
null,
- ContactsContract.Data.MIMETYPE + " = ?",
+ ContactsContract.Data.MIMETYPE + " = ? OR "
+ + ContactsContract.Data.MIMETYPE + " = ?",
new String[]{
- ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE},
ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
mContactListAsyncQueryLiveData = new AsyncQueryLiveData<List<Contact>>(mContext,
QueryParam.of(contactListQueryParam), Executors.newSingleThreadExecutor()) {
@@ -148,11 +158,11 @@
}
/**
- * Looks up a {@link Contact} by the given lookup key. Returns null if can't find the contact
- * entry.
+ * Looks up a {@link Contact} by the given lookup key and account name. Returns null if can't
+ * find the contact entry.
*/
@Nullable
- public Contact lookupContactByKey(String lookupKey) {
+ public Contact lookupContactByKey(String lookupKey, String accountName) {
if (!isLoaded()) {
Log.w(TAG, "looking up a contact while loading.");
}
@@ -160,37 +170,40 @@
Log.w(TAG, "looking up an empty lookup key.");
return null;
}
+ if (TextUtils.isEmpty(accountName)) {
+ Log.w(TAG, "looking up an empty lookup account");
+ return null;
+ }
- return mLookupKeyContactMap.get(lookupKey);
+ return mLookupKeyContactMap.get(getContactKey(lookupKey, accountName));
}
private List<Contact> onCursorLoaded(Cursor cursor) {
- Map<String, Contact> result = new LinkedHashMap<>();
- List<Contact> contacts = new ArrayList<>();
+ Map<String, Contact> contactMap = new LinkedHashMap<>();
+ List<Contact> contactList = new ArrayList<>();
while (cursor.moveToNext()) {
- Contact contact = Contact.fromCursor(mContext, cursor);
- String lookupKey = contact.getLookupKey();
- if (result.containsKey(lookupKey)) {
- Contact existingContact = result.get(lookupKey);
- existingContact.merge(contact);
- } else {
- result.put(lookupKey, contact);
- }
+ int accountNameColumn = cursor.getColumnIndex(
+ ContactsContract.RawContacts.ACCOUNT_NAME);
+ int lookupKeyColumn = cursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY);
+ String accountName = cursor.getString(accountNameColumn);
+ String lookupKey = cursor.getString(lookupKeyColumn);
+ String key = getContactKey(lookupKey, accountName);
+
+ contactMap.put(key, Contact.fromCursor(mContext, cursor, contactMap.get(key)));
}
- contacts.addAll(result.values());
+ contactList.addAll(contactMap.values());
mLookupKeyContactMap.clear();
- mLookupKeyContactMap.putAll(result);
+ mLookupKeyContactMap.putAll(contactMap);
- mPhoneNumberContactMap.clear();
- for (Contact contact : contacts) {
+ for (Contact contact : contactList) {
for (PhoneNumber phoneNumber : contact.getNumbers()) {
mPhoneNumberContactMap.put(phoneNumber.getI18nPhoneNumberWrapper(), contact);
}
}
- return contacts;
+ return contactList;
}
@Override
@@ -198,4 +211,13 @@
Log.d(TAG, "Contacts loaded:" + (contacts == null ? 0 : contacts.size()));
mIsLoaded = true;
}
+
+ /**
+ * Formats a key to identify a contact based the lookup key and the account name.
+ */
+ private String getContactKey(String lookupKey, String accountName) {
+ String key = String.format(KEY_FORMAT, lookupKey, accountName);
+ Log.d(TAG, "Contact key is: " + key);
+ return key;
+ }
}