blob: 0059fece5fe1af7a9f0a1726713fddf0234cc99a [file] [log] [blame]
Yorke Lee2644d942013-10-28 11:05:43 -07001/*
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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.model;
Yorke Lee2644d942013-10-28 11:05:43 -070018
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.Entity;
22import android.net.Uri;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.provider.ContactsContract.Contacts;
26import android.provider.ContactsContract.Data;
27import android.provider.ContactsContract.RawContacts;
28
Gary Mai69c182a2016-12-05 13:07:03 -080029import com.android.contacts.model.account.AccountType;
30import com.android.contacts.model.account.AccountWithDataSet;
31import com.android.contacts.model.dataitem.DataItem;
Gary Mai0a49afa2016-12-05 15:53:58 -080032
Yorke Lee2644d942013-10-28 11:05:43 -070033import com.google.common.base.Objects;
34import com.google.common.collect.Lists;
35
36import java.util.ArrayList;
37import java.util.List;
38
39/**
40 * RawContact represents a single raw contact in the raw contacts database.
41 * It has specialized getters/setters for raw contact
42 * items, and also contains a collection of DataItem objects. A RawContact contains the information
43 * from a single account.
44 *
45 * This allows RawContact objects to be thought of as a class with raw contact
46 * fields (like account type, name, data set, sync state, etc.) and a list of
47 * DataItem objects that represent contact information elements (like phone
48 * numbers, email, address, etc.).
49 */
50final public class RawContact implements Parcelable {
51
52 private AccountTypeManager mAccountTypeManager;
53 private final ContentValues mValues;
54 private final ArrayList<NamedDataItem> mDataItems;
55
56 final public static class NamedDataItem implements Parcelable {
57 public final Uri mUri;
58
59 // This use to be a DataItem. DataItem creation is now delayed until the point of request
60 // since there is no benefit to storing them here due to the multiple inheritance.
61 // Eventually instanceof still has to be used anyways to determine which sub-class of
62 // DataItem it is. And having parent DataItem's here makes it very difficult to serialize or
63 // parcelable.
64 //
65 // Instead of having a common DataItem super class, we should refactor this to be a generic
66 // Object where the object is a concrete class that no longer relies on ContentValues.
67 // (this will also make the classes easier to use).
68 // Since instanceof is used later anyways, having a list of Objects won't hurt and is no
69 // worse than having a DataItem.
70 public final ContentValues mContentValues;
71
72 public NamedDataItem(Uri uri, ContentValues values) {
73 this.mUri = uri;
74 this.mContentValues = values;
75 }
76
77 public NamedDataItem(Parcel parcel) {
78 this.mUri = parcel.readParcelable(Uri.class.getClassLoader());
79 this.mContentValues = parcel.readParcelable(ContentValues.class.getClassLoader());
80 }
81
82 @Override
83 public int describeContents() {
84 return 0;
85 }
86
87 @Override
88 public void writeToParcel(Parcel parcel, int i) {
89 parcel.writeParcelable(mUri, i);
90 parcel.writeParcelable(mContentValues, i);
91 }
92
93 public static final Parcelable.Creator<NamedDataItem> CREATOR
94 = new Parcelable.Creator<NamedDataItem>() {
95
96 @Override
97 public NamedDataItem createFromParcel(Parcel parcel) {
98 return new NamedDataItem(parcel);
99 }
100
101 @Override
102 public NamedDataItem[] newArray(int i) {
103 return new NamedDataItem[i];
104 }
105 };
106
107 @Override
108 public int hashCode() {
109 return Objects.hashCode(mUri, mContentValues);
110 }
111
112 @Override
113 public boolean equals(Object obj) {
114 if (obj == null) return false;
115 if (getClass() != obj.getClass()) return false;
116
117 final NamedDataItem other = (NamedDataItem) obj;
118 return Objects.equal(mUri, other.mUri) &&
119 Objects.equal(mContentValues, other.mContentValues);
120 }
121 }
122
123 public static RawContact createFrom(Entity entity) {
124 final ContentValues values = entity.getEntityValues();
125 final ArrayList<Entity.NamedContentValues> subValues = entity.getSubValues();
126
127 RawContact rawContact = new RawContact(values);
128 for (Entity.NamedContentValues subValue : subValues) {
129 rawContact.addNamedDataItemValues(subValue.uri, subValue.values);
130 }
131 return rawContact;
132 }
133
134 /**
135 * A RawContact object can be created with or without a context.
136 */
137 public RawContact() {
138 this(new ContentValues());
139 }
140
141 public RawContact(ContentValues values) {
142 mValues = values;
143 mDataItems = new ArrayList<NamedDataItem>();
144 }
145
146 /**
147 * Constructor for the parcelable.
148 *
149 * @param parcel The parcel to de-serialize from.
150 */
151 private RawContact(Parcel parcel) {
152 mValues = parcel.readParcelable(ContentValues.class.getClassLoader());
153 mDataItems = Lists.newArrayList();
154 parcel.readTypedList(mDataItems, NamedDataItem.CREATOR);
155 }
156
157 @Override
158 public int describeContents() {
159 return 0;
160 }
161
162 @Override
163 public void writeToParcel(Parcel parcel, int i) {
164 parcel.writeParcelable(mValues, i);
165 parcel.writeTypedList(mDataItems);
166 }
167
168 /**
169 * Create for building the parcelable.
170 */
171 public static final Parcelable.Creator<RawContact> CREATOR
172 = new Parcelable.Creator<RawContact>() {
173
174 @Override
175 public RawContact createFromParcel(Parcel parcel) {
176 return new RawContact(parcel);
177 }
178
179 @Override
180 public RawContact[] newArray(int i) {
181 return new RawContact[i];
182 }
183 };
184
185 public AccountTypeManager getAccountTypeManager(Context context) {
186 if (mAccountTypeManager == null) {
187 mAccountTypeManager = AccountTypeManager.getInstance(context);
188 }
189 return mAccountTypeManager;
190 }
191
192 public ContentValues getValues() {
193 return mValues;
194 }
195
196 /**
197 * Returns the id of the raw contact.
198 */
199 public Long getId() {
200 return getValues().getAsLong(RawContacts._ID);
201 }
202
203 /**
204 * Returns the account name of the raw contact.
205 */
206 public String getAccountName() {
207 return getValues().getAsString(RawContacts.ACCOUNT_NAME);
208 }
209
210 /**
211 * Returns the account type of the raw contact.
212 */
213 public String getAccountTypeString() {
214 return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
215 }
216
217 /**
218 * Returns the data set of the raw contact.
219 */
220 public String getDataSet() {
221 return getValues().getAsString(RawContacts.DATA_SET);
222 }
223
Yorke Lee2644d942013-10-28 11:05:43 -0700224 public boolean isDirty() {
225 return getValues().getAsBoolean(RawContacts.DIRTY);
226 }
227
228 public String getSourceId() {
229 return getValues().getAsString(RawContacts.SOURCE_ID);
230 }
231
232 public String getSync1() {
233 return getValues().getAsString(RawContacts.SYNC1);
234 }
235
236 public String getSync2() {
237 return getValues().getAsString(RawContacts.SYNC2);
238 }
239
240 public String getSync3() {
241 return getValues().getAsString(RawContacts.SYNC3);
242 }
243
244 public String getSync4() {
245 return getValues().getAsString(RawContacts.SYNC4);
246 }
247
248 public boolean isDeleted() {
249 return getValues().getAsBoolean(RawContacts.DELETED);
250 }
251
Yorke Lee2644d942013-10-28 11:05:43 -0700252 public long getContactId() {
253 return getValues().getAsLong(Contacts.Entity.CONTACT_ID);
254 }
255
256 public boolean isStarred() {
257 return getValues().getAsBoolean(Contacts.STARRED);
258 }
259
260 public AccountType getAccountType(Context context) {
261 return getAccountTypeManager(context).getAccountType(getAccountTypeString(), getDataSet());
262 }
263
264 /**
265 * Sets the account name, account type, and data set strings.
266 * Valid combinations for account-name, account-type, data-set
267 * 1) null, null, null (local account)
268 * 2) non-null, non-null, null (valid account without data-set)
269 * 3) non-null, non-null, non-null (valid account with data-set)
270 */
271 private void setAccount(String accountName, String accountType, String dataSet) {
272 final ContentValues values = getValues();
273 if (accountName == null) {
274 if (accountType == null && dataSet == null) {
275 // This is a local account
276 values.putNull(RawContacts.ACCOUNT_NAME);
277 values.putNull(RawContacts.ACCOUNT_TYPE);
278 values.putNull(RawContacts.DATA_SET);
279 return;
280 }
281 } else {
282 if (accountType != null) {
283 // This is a valid account, either with or without a dataSet.
284 values.put(RawContacts.ACCOUNT_NAME, accountName);
285 values.put(RawContacts.ACCOUNT_TYPE, accountType);
286 if (dataSet == null) {
287 values.putNull(RawContacts.DATA_SET);
288 } else {
289 values.put(RawContacts.DATA_SET, dataSet);
290 }
291 return;
292 }
293 }
294 throw new IllegalArgumentException(
295 "Not a valid combination of account name, type, and data set.");
296 }
297
298 public void setAccount(AccountWithDataSet accountWithDataSet) {
Jay Shrauner2ae200d2015-01-09 11:36:20 -0800299 if (accountWithDataSet != null) {
300 setAccount(accountWithDataSet.name, accountWithDataSet.type,
301 accountWithDataSet.dataSet);
302 } else {
303 setAccount(null, null, null);
304 }
Yorke Lee2644d942013-10-28 11:05:43 -0700305 }
306
307 public void setAccountToLocal() {
308 setAccount(null, null, null);
309 }
310
311 /**
312 * Creates and inserts a DataItem object that wraps the content values, and returns it.
313 */
314 public void addDataItemValues(ContentValues values) {
315 addNamedDataItemValues(Data.CONTENT_URI, values);
316 }
317
318 public NamedDataItem addNamedDataItemValues(Uri uri, ContentValues values) {
319 final NamedDataItem namedItem = new NamedDataItem(uri, values);
320 mDataItems.add(namedItem);
321 return namedItem;
322 }
323
324 public ArrayList<ContentValues> getContentValues() {
325 final ArrayList<ContentValues> list = Lists.newArrayListWithCapacity(mDataItems.size());
326 for (NamedDataItem dataItem : mDataItems) {
327 if (Data.CONTENT_URI.equals(dataItem.mUri)) {
328 list.add(dataItem.mContentValues);
329 }
330 }
331 return list;
332 }
333
334 public List<DataItem> getDataItems() {
335 final ArrayList<DataItem> list = Lists.newArrayListWithCapacity(mDataItems.size());
336 for (NamedDataItem dataItem : mDataItems) {
337 if (Data.CONTENT_URI.equals(dataItem.mUri)) {
338 list.add(DataItem.createFrom(dataItem.mContentValues));
339 }
340 }
341 return list;
342 }
343
344 public String toString() {
345 final StringBuilder sb = new StringBuilder();
346 sb.append("RawContact: ").append(mValues);
347 for (RawContact.NamedDataItem namedDataItem : mDataItems) {
348 sb.append("\n ").append(namedDataItem.mUri);
349 sb.append("\n -> ").append(namedDataItem.mContentValues);
350 }
351 return sb.toString();
352 }
353
354 @Override
355 public int hashCode() {
356 return Objects.hashCode(mValues, mDataItems);
357 }
358
359 @Override
360 public boolean equals(Object obj) {
361 if (obj == null) return false;
362 if (getClass() != obj.getClass()) return false;
363
364 RawContact other = (RawContact) obj;
365 return Objects.equal(mValues, other.mValues) &&
366 Objects.equal(mDataItems, other.mDataItems);
367 }
368}