1 /* 2 * Copyright (C) 2012 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.common.model; 18 19 import android.content.ContentValues; 20 import android.net.Uri; 21 import android.provider.ContactsContract.CommonDataKinds.Photo; 22 import android.provider.ContactsContract.Data; 23 import android.provider.ContactsContract.Directory; 24 import android.provider.ContactsContract.DisplayNameSources; 25 import android.support.annotation.VisibleForTesting; 26 import com.android.contacts.common.GroupMetaData; 27 import com.android.contacts.common.model.account.AccountType; 28 import com.google.common.collect.ImmutableList; 29 import java.util.ArrayList; 30 31 /** 32 * A Contact represents a single person or logical entity as perceived by the user. The information 33 * about a contact can come from multiple data sources, which are each represented by a RawContact 34 * object. Thus, a Contact is associated with a collection of RawContact objects. 35 * 36 * <p>The aggregation of raw contacts into a single contact is performed automatically, and it is 37 * also possible for users to manually split and join raw contacts into various contacts. 38 * 39 * <p>Only the {@link ContactLoader} class can create a Contact object with various flags to allow 40 * partial loading of contact data. Thus, an instance of this class should be treated as a read-only 41 * object. 42 */ 43 public class Contact { 44 45 private final Uri mRequestedUri; 46 private final Uri mLookupUri; 47 private final Uri mUri; 48 private final long mDirectoryId; 49 private final String mLookupKey; 50 private final long mId; 51 private final long mNameRawContactId; 52 private final int mDisplayNameSource; 53 private final long mPhotoId; 54 private final String mPhotoUri; 55 private final String mDisplayName; 56 private final String mAltDisplayName; 57 private final String mPhoneticName; 58 private final boolean mStarred; 59 private final Integer mPresence; 60 private final boolean mSendToVoicemail; 61 private final String mCustomRingtone; 62 private final boolean mIsUserProfile; 63 private final Contact.Status mStatus; 64 private final Exception mException; 65 private ImmutableList<RawContact> mRawContacts; 66 private ImmutableList<AccountType> mInvitableAccountTypes; 67 private String mDirectoryDisplayName; 68 private String mDirectoryType; 69 private String mDirectoryAccountType; 70 private String mDirectoryAccountName; 71 private int mDirectoryExportSupport; 72 private ImmutableList<GroupMetaData> mGroups; 73 private byte[] mPhotoBinaryData; 74 /** 75 * Small version of the contact photo loaded from a blob instead of from a file. If a large 76 * contact photo is not available yet, then this has the same value as mPhotoBinaryData. 77 */ 78 private byte[] mThumbnailPhotoBinaryData; 79 80 /** Constructor for special results, namely "no contact found" and "error". */ Contact(Uri requestedUri, Contact.Status status, Exception exception)81 private Contact(Uri requestedUri, Contact.Status status, Exception exception) { 82 if (status == Status.ERROR && exception == null) { 83 throw new IllegalArgumentException("ERROR result must have exception"); 84 } 85 mStatus = status; 86 mException = exception; 87 mRequestedUri = requestedUri; 88 mLookupUri = null; 89 mUri = null; 90 mDirectoryId = -1; 91 mLookupKey = null; 92 mId = -1; 93 mRawContacts = null; 94 mNameRawContactId = -1; 95 mDisplayNameSource = DisplayNameSources.UNDEFINED; 96 mPhotoId = -1; 97 mPhotoUri = null; 98 mDisplayName = null; 99 mAltDisplayName = null; 100 mPhoneticName = null; 101 mStarred = false; 102 mPresence = null; 103 mInvitableAccountTypes = null; 104 mSendToVoicemail = false; 105 mCustomRingtone = null; 106 mIsUserProfile = false; 107 } 108 109 /** Constructor to call when contact was found */ Contact( Uri requestedUri, Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id, long nameRawContactId, int displayNameSource, long photoId, String photoUri, String displayName, String altDisplayName, String phoneticName, boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone, boolean isUserProfile)110 public Contact( 111 Uri requestedUri, 112 Uri uri, 113 Uri lookupUri, 114 long directoryId, 115 String lookupKey, 116 long id, 117 long nameRawContactId, 118 int displayNameSource, 119 long photoId, 120 String photoUri, 121 String displayName, 122 String altDisplayName, 123 String phoneticName, 124 boolean starred, 125 Integer presence, 126 boolean sendToVoicemail, 127 String customRingtone, 128 boolean isUserProfile) { 129 mStatus = Status.LOADED; 130 mException = null; 131 mRequestedUri = requestedUri; 132 mLookupUri = lookupUri; 133 mUri = uri; 134 mDirectoryId = directoryId; 135 mLookupKey = lookupKey; 136 mId = id; 137 mRawContacts = null; 138 mNameRawContactId = nameRawContactId; 139 mDisplayNameSource = displayNameSource; 140 mPhotoId = photoId; 141 mPhotoUri = photoUri; 142 mDisplayName = displayName; 143 mAltDisplayName = altDisplayName; 144 mPhoneticName = phoneticName; 145 mStarred = starred; 146 mPresence = presence; 147 mInvitableAccountTypes = null; 148 mSendToVoicemail = sendToVoicemail; 149 mCustomRingtone = customRingtone; 150 mIsUserProfile = isUserProfile; 151 } 152 Contact(Uri requestedUri, Contact from)153 public Contact(Uri requestedUri, Contact from) { 154 mRequestedUri = requestedUri; 155 156 mStatus = from.mStatus; 157 mException = from.mException; 158 mLookupUri = from.mLookupUri; 159 mUri = from.mUri; 160 mDirectoryId = from.mDirectoryId; 161 mLookupKey = from.mLookupKey; 162 mId = from.mId; 163 mNameRawContactId = from.mNameRawContactId; 164 mDisplayNameSource = from.mDisplayNameSource; 165 mPhotoId = from.mPhotoId; 166 mPhotoUri = from.mPhotoUri; 167 mDisplayName = from.mDisplayName; 168 mAltDisplayName = from.mAltDisplayName; 169 mPhoneticName = from.mPhoneticName; 170 mStarred = from.mStarred; 171 mPresence = from.mPresence; 172 mRawContacts = from.mRawContacts; 173 mInvitableAccountTypes = from.mInvitableAccountTypes; 174 175 mDirectoryDisplayName = from.mDirectoryDisplayName; 176 mDirectoryType = from.mDirectoryType; 177 mDirectoryAccountType = from.mDirectoryAccountType; 178 mDirectoryAccountName = from.mDirectoryAccountName; 179 mDirectoryExportSupport = from.mDirectoryExportSupport; 180 181 mGroups = from.mGroups; 182 183 mPhotoBinaryData = from.mPhotoBinaryData; 184 mSendToVoicemail = from.mSendToVoicemail; 185 mCustomRingtone = from.mCustomRingtone; 186 mIsUserProfile = from.mIsUserProfile; 187 } 188 forError(Uri requestedUri, Exception exception)189 public static Contact forError(Uri requestedUri, Exception exception) { 190 return new Contact(requestedUri, Status.ERROR, exception); 191 } 192 forNotFound(Uri requestedUri)193 public static Contact forNotFound(Uri requestedUri) { 194 return new Contact(requestedUri, Status.NOT_FOUND, null); 195 } 196 197 /** @param exportSupport See {@link Directory#EXPORT_SUPPORT}. */ setDirectoryMetaData( String displayName, String directoryType, String accountType, String accountName, int exportSupport)198 public void setDirectoryMetaData( 199 String displayName, 200 String directoryType, 201 String accountType, 202 String accountName, 203 int exportSupport) { 204 mDirectoryDisplayName = displayName; 205 mDirectoryType = directoryType; 206 mDirectoryAccountType = accountType; 207 mDirectoryAccountName = accountName; 208 mDirectoryExportSupport = exportSupport; 209 } 210 211 /** 212 * Returns the URI for the contact that contains both the lookup key and the ID. This is the best 213 * URI to reference a contact. For directory contacts, this is the same a the URI as returned by 214 * {@link #getUri()} 215 */ getLookupUri()216 public Uri getLookupUri() { 217 return mLookupUri; 218 } 219 getLookupKey()220 public String getLookupKey() { 221 return mLookupKey; 222 } 223 224 /** 225 * Returns the contact Uri that was passed to the provider to make the query. This is the same as 226 * the requested Uri, unless the requested Uri doesn't specify a Contact: If it either references 227 * a Raw-Contact or a Person (a pre-Eclair style Uri), this Uri will always reference the full 228 * aggregate contact. 229 */ getUri()230 public Uri getUri() { 231 return mUri; 232 } 233 234 /** Returns the contact ID. */ 235 @VisibleForTesting getId()236 public long getId() { 237 return mId; 238 } 239 240 /** 241 * @return true when an exception happened during loading, in which case {@link #getException} 242 * returns the actual exception object. 243 */ isError()244 public boolean isError() { 245 return mStatus == Status.ERROR; 246 } 247 getException()248 public Exception getException() { 249 return mException; 250 } 251 252 /** @return true if the specified contact is successfully loaded. */ isLoaded()253 public boolean isLoaded() { 254 return mStatus == Status.LOADED; 255 } 256 getNameRawContactId()257 public long getNameRawContactId() { 258 return mNameRawContactId; 259 } 260 getDisplayNameSource()261 public int getDisplayNameSource() { 262 return mDisplayNameSource; 263 } 264 getPhotoId()265 public long getPhotoId() { 266 return mPhotoId; 267 } 268 getPhotoUri()269 public String getPhotoUri() { 270 return mPhotoUri; 271 } 272 getDisplayName()273 public String getDisplayName() { 274 return mDisplayName; 275 } 276 getStarred()277 public boolean getStarred() { 278 return mStarred; 279 } 280 getPresence()281 public Integer getPresence() { 282 return mPresence; 283 } 284 285 /** 286 * This can return non-null invitable account types only if the {@link ContactLoader} was 287 * configured to load invitable account types in its constructor. 288 */ getInvitableAccountTypes()289 public ImmutableList<AccountType> getInvitableAccountTypes() { 290 return mInvitableAccountTypes; 291 } 292 setInvitableAccountTypes(ImmutableList<AccountType> accountTypes)293 /* package */ void setInvitableAccountTypes(ImmutableList<AccountType> accountTypes) { 294 mInvitableAccountTypes = accountTypes; 295 } 296 getRawContacts()297 public ImmutableList<RawContact> getRawContacts() { 298 return mRawContacts; 299 } 300 setRawContacts(ImmutableList<RawContact> rawContacts)301 /* package */ void setRawContacts(ImmutableList<RawContact> rawContacts) { 302 mRawContacts = rawContacts; 303 } 304 getDirectoryId()305 public long getDirectoryId() { 306 return mDirectoryId; 307 } 308 isDirectoryEntry()309 public boolean isDirectoryEntry() { 310 return mDirectoryId != -1 311 && mDirectoryId != Directory.DEFAULT 312 && mDirectoryId != Directory.LOCAL_INVISIBLE; 313 } 314 setPhotoBinaryData(byte[] photoBinaryData)315 /* package */ void setPhotoBinaryData(byte[] photoBinaryData) { 316 mPhotoBinaryData = photoBinaryData; 317 } 318 getThumbnailPhotoBinaryData()319 public byte[] getThumbnailPhotoBinaryData() { 320 return mThumbnailPhotoBinaryData; 321 } 322 setThumbnailPhotoBinaryData(byte[] photoBinaryData)323 /* package */ void setThumbnailPhotoBinaryData(byte[] photoBinaryData) { 324 mThumbnailPhotoBinaryData = photoBinaryData; 325 } 326 getContentValues()327 public ArrayList<ContentValues> getContentValues() { 328 if (mRawContacts.size() != 1) { 329 throw new IllegalStateException("Cannot extract content values from an aggregated contact"); 330 } 331 332 RawContact rawContact = mRawContacts.get(0); 333 ArrayList<ContentValues> result = rawContact.getContentValues(); 334 335 // If the photo was loaded using the URI, create an entry for the photo 336 // binary data. 337 if (mPhotoId == 0 && mPhotoBinaryData != null) { 338 ContentValues photo = new ContentValues(); 339 photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 340 photo.put(Photo.PHOTO, mPhotoBinaryData); 341 result.add(photo); 342 } 343 344 return result; 345 } 346 347 /** 348 * This can return non-null group meta-data only if the {@link ContactLoader} was configured to 349 * load group metadata in its constructor. 350 */ getGroupMetaData()351 public ImmutableList<GroupMetaData> getGroupMetaData() { 352 return mGroups; 353 } 354 setGroupMetaData(ImmutableList<GroupMetaData> groups)355 /* package */ void setGroupMetaData(ImmutableList<GroupMetaData> groups) { 356 mGroups = groups; 357 } 358 isUserProfile()359 public boolean isUserProfile() { 360 return mIsUserProfile; 361 } 362 363 @Override toString()364 public String toString() { 365 return "{requested=" 366 + mRequestedUri 367 + ",lookupkey=" 368 + mLookupKey 369 + ",uri=" 370 + mUri 371 + ",status=" 372 + mStatus 373 + "}"; 374 } 375 376 private enum Status { 377 /** Contact is successfully loaded */ 378 LOADED, 379 /** There was an error loading the contact */ 380 ERROR, 381 /** Contact is not found */ 382 NOT_FOUND, 383 } 384 } 385