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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 190 public int getEntryType() { 191 return mEntryType; 192 } 193 194 public String getDisplayName() { 195 return mDisplayName; 196 } 197 198 public String getDestination() { 199 return mDestination; 200 } 201 202 public int getDestinationType() { 203 return mDestinationType; 204 } 205 206 public String getDestinationLabel() { 207 return mDestinationLabel; 208 } 209 210 public long getContactId() { 211 return mContactId; 212 } 213 214 public Long getDirectoryId() { 215 return mDirectoryId; 216 } 217 218 public long getDataId() { 219 return mDataId; 220 } 221 222 public boolean isFirstLevel() { 223 return mIsFirstLevel; 224 } 225 226 public Uri getPhotoThumbnailUri() { 227 return mPhotoThumbnailUri; 228 } 229 230 /** This can be called outside main Looper thread. */ 231 public synchronized void setPhotoBytes(byte[] photoBytes) { 232 mPhotoBytes = photoBytes; 233 } 234 235 /** This can be called outside main Looper thread. */ 236 public synchronized byte[] getPhotoBytes() { 237 return mPhotoBytes; 238 } 239 240 public boolean isSeparator() { 241 return mIsDivider; 242 } 243 244 public boolean isSelectable() { 245 return mEntryType == ENTRY_TYPE_PERSON; 246 } 247 248 public String getLookupKey() { 249 return mLookupKey; 250 } 251 252 @Override 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 */ 261 public boolean isSamePerson(final RecipientEntry entry) { 262 return entry != null && mContactId == entry.mContactId; 263 } 264 }