1 /* 2 * Copyright (C) 2011 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.ex.chips; 18 19 import android.net.Uri; 20 import android.provider.ContactsContract.CommonDataKinds.Email; 21 import android.provider.ContactsContract.DisplayNameSources; 22 import android.text.util.Rfc822Token; 23 import android.text.util.Rfc822Tokenizer; 24 25 /** 26 * Represents one entry inside recipient auto-complete list. 27 */ 28 public class RecipientEntry { 29 /* package */ static final int INVALID_CONTACT = -1; 30 /** 31 * A GENERATED_CONTACT is one that was created based entirely on 32 * information passed in to the RecipientEntry from an external source 33 * that is not a real contact. 34 */ 35 /* package */ static final int GENERATED_CONTACT = -2; 36 37 /** Used when {@link #mDestinationType} is invalid and thus shouldn't be used for display. */ 38 public static final int INVALID_DESTINATION_TYPE = -1; 39 40 public static final int ENTRY_TYPE_PERSON = 0; 41 42 public static final int ENTRY_TYPE_SIZE = 1; 43 44 private final int mEntryType; 45 46 /** 47 * True when this entry is the first entry in a group, which should have a photo and display 48 * name, while the second or later entries won't. 49 */ 50 private boolean mIsFirstLevel; 51 private final String mDisplayName; 52 53 /** Destination for this contact entry. Would be an email address or a phone number. */ 54 private final String mDestination; 55 /** Type of the destination like {@link Email#TYPE_HOME} */ 56 private final int mDestinationType; 57 /** 58 * Label of the destination which will be used when type was {@link Email#TYPE_CUSTOM}. 59 * Can be null when {@link #mDestinationType} is {@link #INVALID_DESTINATION_TYPE}. 60 */ 61 private final String mDestinationLabel; 62 /** ID for the person */ 63 private final long mContactId; 64 /** ID for the directory this contact came from, or <code>null</code> */ 65 private final Long mDirectoryId; 66 /** ID for the destination */ 67 private final long mDataId; 68 private final boolean mIsDivider; 69 70 private final Uri mPhotoThumbnailUri; 71 72 private boolean mIsValid; 73 /** 74 * This can be updated after this object being constructed, when the photo is fetched 75 * from remote directories. 76 */ 77 private byte[] mPhotoBytes; 78 79 /** See {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} */ 80 private final String mLookupKey; 81 RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, String lookupKey)82 protected RecipientEntry(int entryType, String displayName, String destination, 83 int destinationType, String destinationLabel, long contactId, Long directoryId, 84 long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, 85 String lookupKey) { 86 mEntryType = entryType; 87 mIsFirstLevel = isFirstLevel; 88 mDisplayName = displayName; 89 mDestination = destination; 90 mDestinationType = destinationType; 91 mDestinationLabel = destinationLabel; 92 mContactId = contactId; 93 mDirectoryId = directoryId; 94 mDataId = dataId; 95 mPhotoThumbnailUri = photoThumbnailUri; 96 mPhotoBytes = null; 97 mIsDivider = false; 98 mIsValid = isValid; 99 mLookupKey = lookupKey; 100 } 101 isValid()102 public boolean isValid() { 103 return mIsValid; 104 } 105 106 /** 107 * Determine if this was a RecipientEntry created from recipient info or 108 * an entry from contacts. 109 */ isCreatedRecipient(long id)110 public static boolean isCreatedRecipient(long id) { 111 return id == RecipientEntry.INVALID_CONTACT || id == RecipientEntry.GENERATED_CONTACT; 112 } 113 114 /** 115 * Construct a RecipientEntry from just an address that has been entered. 116 * This address has not been resolved to a contact and therefore does not 117 * have a contact id or photo. 118 */ constructFakeEntry(final String address, final boolean isValid)119 public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) { 120 final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address); 121 final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address; 122 123 return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress, 124 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */, 125 INVALID_CONTACT, null, true, isValid, null /* lookupKey */); 126 } 127 128 /** 129 * Construct a RecipientEntry from just a phone number. 130 */ constructFakePhoneEntry(final String phoneNumber, final boolean isValid)131 public static RecipientEntry constructFakePhoneEntry(final String phoneNumber, 132 final boolean isValid) { 133 return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber, 134 INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */, 135 INVALID_CONTACT, null, true, isValid, null /* lookupKey */); 136 } 137 138 /** 139 * @return the display name for the entry. If the display name source is larger than 140 * {@link DisplayNameSources#PHONE} we use the contact's display name, but if not, 141 * i.e. the display name came from an email address or a phone number, we don't use it 142 * to avoid confusion and just use the destination instead. 143 */ pickDisplayName(int displayNameSource, String displayName, String destination)144 private static String pickDisplayName(int displayNameSource, String displayName, 145 String destination) { 146 return (displayNameSource > DisplayNameSources.PHONE) ? displayName : destination; 147 } 148 149 /** 150 * Construct a RecipientEntry from just an address that has been entered 151 * with both an associated display name. This address has not been resolved 152 * to a contact and therefore does not have a contact id or photo. 153 */ constructGeneratedEntry(String display, String address, boolean isValid)154 public static RecipientEntry constructGeneratedEntry(String display, String address, 155 boolean isValid) { 156 return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE, 157 null, GENERATED_CONTACT, null /* directoryId */, GENERATED_CONTACT, null, true, 158 isValid, null /* lookupKey */); 159 } 160 constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid, String lookupKey)161 public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, 162 String destination, int destinationType, String destinationLabel, long contactId, 163 Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid, 164 String lookupKey) { 165 return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, 166 displayName, destination), destination, destinationType, destinationLabel, 167 contactId, directoryId, dataId, photoThumbnailUri, true, isValid, lookupKey); 168 } 169 constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)170 public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, 171 String destination, int destinationType, String destinationLabel, long contactId, 172 Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, 173 String lookupKey) { 174 return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, 175 displayName, destination), destination, destinationType, destinationLabel, 176 contactId, directoryId, dataId, (thumbnailUriAsString != null 177 ? Uri.parse(thumbnailUriAsString) : null), true, isValid, lookupKey); 178 } 179 constructSecondLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid, String lookupKey)180 public static RecipientEntry constructSecondLevelEntry(String displayName, 181 int displayNameSource, String destination, int destinationType, 182 String destinationLabel, long contactId, Long directoryId, long dataId, 183 String thumbnailUriAsString, boolean isValid, String lookupKey) { 184 return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, 185 displayName, destination), destination, destinationType, destinationLabel, 186 contactId, directoryId, dataId, (thumbnailUriAsString != null 187 ? Uri.parse(thumbnailUriAsString) : null), false, isValid, lookupKey); 188 } 189 getEntryType()190 public int getEntryType() { 191 return mEntryType; 192 } 193 getDisplayName()194 public String getDisplayName() { 195 return mDisplayName; 196 } 197 getDestination()198 public String getDestination() { 199 return mDestination; 200 } 201 getDestinationType()202 public int getDestinationType() { 203 return mDestinationType; 204 } 205 getDestinationLabel()206 public String getDestinationLabel() { 207 return mDestinationLabel; 208 } 209 getContactId()210 public long getContactId() { 211 return mContactId; 212 } 213 getDirectoryId()214 public Long getDirectoryId() { 215 return mDirectoryId; 216 } 217 getDataId()218 public long getDataId() { 219 return mDataId; 220 } 221 isFirstLevel()222 public boolean isFirstLevel() { 223 return mIsFirstLevel; 224 } 225 getPhotoThumbnailUri()226 public Uri getPhotoThumbnailUri() { 227 return mPhotoThumbnailUri; 228 } 229 230 /** This can be called outside main Looper thread. */ setPhotoBytes(byte[] photoBytes)231 public synchronized void setPhotoBytes(byte[] photoBytes) { 232 mPhotoBytes = photoBytes; 233 } 234 235 /** This can be called outside main Looper thread. */ getPhotoBytes()236 public synchronized byte[] getPhotoBytes() { 237 return mPhotoBytes; 238 } 239 isSeparator()240 public boolean isSeparator() { 241 return mIsDivider; 242 } 243 isSelectable()244 public boolean isSelectable() { 245 return mEntryType == ENTRY_TYPE_PERSON; 246 } 247 getLookupKey()248 public String getLookupKey() { 249 return mLookupKey; 250 } 251 252 @Override toString()253 public String toString() { 254 return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid; 255 } 256 257 /** 258 * Returns if entry represents the same person as this instance. The default implementation 259 * checks whether the contact ids are the same, and subclasses may opt to override this. 260 */ isSamePerson(final RecipientEntry entry)261 public boolean isSamePerson(final RecipientEntry entry) { 262 return entry != null && mContactId == entry.mContactId; 263 } 264 }